[英]Avoid calling constructor of member variable
我有以下C ++ - 類:
// Header-File
class A
{
public:
A();
private:
B m_B;
C m_C;
};
// cpp-File
A::A()
: m_B(1)
{
m_B.doSomething();
m_B.doMore();
m_C = C(m_B.getSomeValue());
}
我現在想避免 class A
調用C m_C
任何構造C m_C
。 因為在A::A()
的最后一行,我無論如何都要自己初始化m_C
因為我需要先准備m_B
。 我可以為class B
提供一個空的默認構造函數。 但那不是主意。
我已經嘗試將m_C(NULL)
添加到A::A()
的init-list中。 有時它工作,有時它說沒有構造函數將NULL
作為參數。
那么我怎么能讓m_C
保持未初始化? 我知道,使用指針, m_C(NULL)
。 而且我不想使用new
動態分配它。
任何想法都表示贊賞。
如何使用本QA中描述的技術?
std::aligned_storage<sizeof(T[n]), alignof(T)>::type
或者,您也可以考慮使用union
。 AFAIK, 聯合只會使用第一個命名成員的構造函數進行初始化。
例如,
union
{
uint8_t _nothing = 0;
C c;
};
根據質量保證中提到的標准, c
將被零初始化,並且不會調用其構造函數。
我沒有看到實現你想要的好方法。 這必須是一個解決方法:
// Header-File
class A
{
public:
A();
private:
B m_B;
C m_C;
static int prepareC(B& b);
};
// cpp-File
A::A()
: m_B(1)
, m_C(prepareC(m_B))
{
}
int A::prepareC(B& b)
{
b.doSomething();
b.doMore();
return b.getSomeValue();
}
請確保m_B.doSomething()
, m_B.doMore()
和m_B.getSomeValue()
不接觸m_C
(直接或間接)。
正如@Tobias正確提到的,此解決方案取決於初始化的順序。 您需要確保m_B
和m_C
的定義按此順序排列。
根據@ Loki的想法更新了代碼。
你不能。
輸入construcotr代碼塊時,所有成員變量都是完整構造的。 這意味着必須調用構造函數。
但你可以解決這個限制。
// Header-File
class A
{
struct Initer
{
Initer(B& b)
: m_b(b)
{
m_b.doSomething();
m_b.doMore();
}
operator int() // assuming getSomeValue() returns int.
{
return m_b.getSomeValue();
}
B& m_b;
};
public:
A();
private: // order important.
B m_B;
C m_C;
};
// cpp-File
A::A()
: m_B(1)
, m_C(Initer(m_B))
{
}
棘手,但可以做到。
您需要的是一種向成員變量添加行為的方法。 所以變量是初始化的,也可能不是。 我們稱之為“可能”
如果以通用方式執行此操作,則需要模板類來封裝該行為並將其應用於任何類型:
template<class T>
Maybe {
public:
Maybe() : m_has(false) {}
// If we want to start with the value, call the constructor
Maybe(const T& v) : m_has(true) { new (m_value) T(v); }
// If we have some value, make sure to call the destructor
˜Maybe() { if (m_has) reinterpret_cast<T*>(m_value)->˜T(); }
// Add the value latter on
void setValue(const T& v) {
if (m_has) {
reinterpret_cast<T>(*m_value) = v;
} else {
m_has = true;
new (m_value) T(v);
}
}
bool hasValue() const { return m_has; }
const T& value() const { return reinterpret_cast<T&>(*m_value); }
T& value() { return reinterpret_cast<T&>(m_value); }
private:
bool m_has;
// Reserve the memory for the object, but dont initialize it - dont call it T
uint8_t m_value[sizeof(T)];
};
我把代碼寫到了我的頭頂,所以可能會有一些拼寫錯誤或小細節需要調整。 我知道它有效。
現在,只需將您的成員稱為Maybe,然后您就不必創建空構造函數。
你要求的是被禁止的 - 而且是正確的。 這可確保正確初始化每個成員。 不要試圖解決它 - 嘗試構建他們使用它的類。
理念:
這里我們有構建基塊:
#include <iostream>
class C
{
public:
C(int i){std::cout << "C::C(" << i << ")" << std::endl;}
};
class B
{
public:
B(int i){std::cout << "B::B(" << i << ")" << std::endl;}
void doSomething(){std::cout << "B::doSomething()" << std::endl;}
void doMore(){std::cout << "B::doMore()" << std::endl;}
int getSomeValue(){return 42;}
};
如果你想為B做一種新的構造,考慮做一個派生類:
class B1 : public B
{
public:
B1() : B(1)
{
doSomething();
doMore();
}
};
現在使用從B派生的B1類:
class A
{
private:
B1 _b;
C _c;
public:
A() : _c(_b.getSomeValue()){std::cout << "A::A()" << std::endl;}
};
然后:
int main()
{
A a;
}
輸出:
B::B(1)
B::doSomething()
B::doMore()
C::C(42)
A::A()
如果您不想使用new
動態分配代碼混亂/異常安全原因,可以使用std::unique_ptr
或std::auto_ptr
來解決此問題。
避免new
的解決方案是編輯C
以進行兩步初始化過程。 然后構造函數將構造一個“zombie”對象,並且您必須在該m_C
實例上調用Initialize
方法來完成初始化。 這與您在可以將NULL
傳遞給構造函數的現有案例類似,然后返回初始化對象。
編輯:
我之前想過這個(盡管它看起來很像其他人的解決方案)。 但是我必須得到一些確認,在我添加這個解決方案之前這不會破壞--C ++可能非常棘手,而且我不經常使用它:)
這比我的其他建議更清晰,並且不要求你搞亂任何實現但是A
。
在初始化時只需使用靜態方法作為中間人:
class A
{
public:
A();
private:
static int InitFromB(B& b)
{
b.doSomething();
b.doMore();
return b.getSomeValue();
}
// m_B must be initialized before m_C
B m_B;
C m_C;
};
A::A()
: m_B(1)
, m_C(InitFromB(m_B))
{
}
請注意,這意味着您不能允許m_B
依賴於A
或C
的實例,而此答案頂部的解決方案可能允許您將A
或m_C
傳遞給m_B
的方法。
最簡單的是存儲指向B
和C
指針。 這些可以初始化為0,省略任何結構。 注意不要取消引用空指針並在A
的析構函數中刪除它(或使用std::unique_ptr
/ boost::scoped_ptr
)。
但是為什么不首先初始化m_B
(通過正確的構造函數調用,而不是在A::A()
,然后使用該初始化的B
實例初始化m_C
?它會調用一個小的重寫,但我敢打賭它值得代碼清理。
指針聽起來像是我唯一干凈的解決方案。 我看到的唯一的另一個解決方案是為C設置一個默認構造函數,它什么都不做,並且在C中有一個初始化方法,你稍后會自己調用。
m_C.Initialise(m_B.getSomeValue());
只需使用逗號表達式:
A::A()
: m_B(1)
, m_c(m_B.doSomething(), m_B.doMore(), m_B.getSomeValue())
{
}
顯然,正如其他人所解釋的那樣, m_B
最好在m_C
之前聲明,否則m_B.doSomething()
調用未定義的行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.