[英]How can I initialize C++ object member variables in the constructor?
我有一個類,它有幾個對象作為成員變量。 我不希望在聲明時調用這些成員的構造函數,因此我試圖明確地掛起指向該對象的指針。 我不知道我在做什么。
我想也許我可以執行以下操作,在初始化對象成員變量時立即調用構造函數:
class MyClass {
public:
MyClass(int n);
private:
AnotherClass another(100); // Construct AnotherClass right away!
};
但我希望MyClass
構造函數調用AnotherClass
構造函數。 這是我的代碼的樣子:
#include "ThingOne.h"
#include "ThingTwo.h"
class BigMommaClass {
public:
BigMommaClass(int numba1, int numba2);
private:
ThingOne* ThingOne;
ThingTwo* ThingTwo;
};
#include "BigMommaClass.h"
BigMommaClass::BigMommaClass(int numba1, int numba2) {
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
}
這是我嘗試編譯時遇到的錯誤:
g++ -Wall -c -Iclasses -o objects/BigMommaClass.o classes/BigMommaClass.cpp
In file included from classes/BigMommaClass.cpp:1:0:
classes/BigMommaClass.h:12:8: error: declaration of âThingTwo* BigMommaClass::ThingTwoâ
classes/ThingTwo.h:1:11: error: changes meaning of âThingTwoâ from âclass ThingTwoâ
classes/BigMommaClass.cpp: In constructor âBigMommaClass::BigMommaClass(int, int)â:
classes/BigMommaClass.cpp:4:30: error: cannot convert âThingOneâ to âThingOne*â in assignment
classes/BigMommaClass.cpp:5:37: error: â((BigMommaClass*)this)->BigMommaClass::ThingTwoâ cannot be used as a function
make: *** [BigMommaClass.o] Error 1
我是否使用了正確的方法,但使用了錯誤的語法? 還是我應該從不同的方向來?
您可以在成員初始值設定項列表中指定如何初始化成員:
BigMommaClass {
BigMommaClass(int, int);
private:
ThingOne thingOne;
ThingTwo thingTwo;
};
BigMommaClass::BigMommaClass(int numba1, int numba2)
: thingOne(numba1 + numba2), thingTwo(numba1, numba2) {}
您正在嘗試使用operator=
創建ThingOne
,這將不起作用(語法不正確)。 此外,您使用類名作為變量名,即ThingOne* ThingOne
。 首先,讓我們修復變量名稱:
private:
ThingOne* t1;
ThingTwo* t2;
由於這些是指針,它們必須指向某些東西。 如果尚未構造對象,則需要在BigMommaClass
構造函數中使用 new 顯式執行此BigMommaClass
:
BigMommaClass::BigMommaClass(int n1, int n2)
{
t1 = new ThingOne(100);
t2 = new ThingTwo(n1, n2);
}
然而,通常初始化列表更適合構造,所以它看起來像:
BigMommaClass::BigMommaClass(int n1, int n2)
: t1(new ThingOne(100)), t2(new ThingTwo(n1, n2))
{ }
這個問題有點老了,但這是 C++11 中在初始化成員變量之前在構造函數中“做更多工作”的另一種方法:
BigMommaClass::BigMommaClass(int numba1, int numba2)
: thingOne([](int n1, int n2){return n1+n2;}(numba1,numba2)),
thingTwo(numba1, numba2) {}
上面的 lambda 函數將被調用並將結果傳遞給 thingOnes 構造函數。 您當然可以根據需要使 lambda 變得復雜。
我知道這是 5 年后的事,但上面的答復並沒有說明您的軟件出了什么問題。 (嗯,Yuushi 的確實如此,但直到我輸入這個我才意識到 - 哦!)。 他們回答標題中的問題如何在構造函數中初始化 C++ 對象成員變量? 這是關於其他問題:我是否使用了正確的方法但使用了錯誤的語法? 還是我應該從不同的方向來?
編程風格在很大程度上取決於意見,但在構造函數中盡可能多做的另一種觀點是將構造函數保持在最低限度,通常具有單獨的初始化函數。 沒有必要嘗試將所有初始化都塞進構造函數中,更不用說有時嘗試將某些東西強制放入構造函數初始化列表中。
那么,說到點子上,你的軟件出了什么問題?
private:
ThingOne* ThingOne;
ThingTwo* ThingTwo;
請注意,在這些行之后, ThingOne
(和ThingTwo
)現在有兩個含義,具體取決於上下文。
在 BigMommaClass 之外, ThingOne
是您使用#include "ThingOne.h"
創建的類
在 BigMommaClass 中, ThingOne
是一個指針。
那是假設編譯器甚至可以理解這些行,並且不會陷入循環,認為ThingOne
是指向某事物的指針,而該指針本身是指向某事物的指針,而該指針是指向 ...
以后寫的時候
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
請記住,在BigMommaClass
內部,您的ThingOne
是一個指針。
如果您更改指針的聲明以包含前綴 (p)
private:
ThingOne* pThingOne;
ThingTwo* pThingTwo;
然后ThingOne
總是引用類和pThingOne
指針。
然后可以重寫
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
作為
pThingOne = new ThingOne(100);
pThingTwo = new ThingTwo(numba1, numba2);
這糾正了兩個問題:雙重含義問題和缺少的new
。 (你可以離開this->
如果你願意!)
有了這些,我可以將以下幾行添加到我的 C++ 程序中,它可以很好地編譯。
class ThingOne{public:ThingOne(int n){};};
class ThingTwo{public:ThingTwo(int x, int y){};};
class BigMommaClass {
public:
BigMommaClass(int numba1, int numba2);
private:
ThingOne* pThingOne;
ThingTwo* pThingTwo;
};
BigMommaClass::BigMommaClass(int numba1, int numba2)
{
pThingOne = new ThingOne(numba1 + numba2);
pThingTwo = new ThingTwo(numba1, numba2);
};
當你寫
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
this->
的使用告訴編譯器左邊的ThingOne
是用來表示指針的。 然而,我們當時在BigMommaClass
內部,這沒有必要。
問題在於等號的右側,其中ThingOne
旨在表示類。 所以另一種糾正你的問題的方法是寫
this->ThingOne = new ::ThingOne(100);
this->ThingTwo = new ::ThingTwo(numba1, numba2);
或者干脆
ThingOne = new ::ThingOne(100);
ThingTwo = new ::ThingTwo(numba1, numba2);
使用::
來改變編譯器對標識符的解釋。
關於克里斯的第一個(也是偉大的)答案,他提出了一種解決方案,以解決類成員被視為“真正的復合”成員(即,不是作為指針或引用)的情況:
該注釋有點大,因此我將在此處使用一些示例代碼進行演示。
當你選擇我提到的成員時,你還要記住這兩件事:
對於每個沒有默認構造函數的“組合對象”-您必須在“父”類的所有構造函數的初始化列表中對其進行初始化(即原始示例中的BigMommaClass
或MyClass
和下面代碼中的MyClass
),如果有幾個(請參閱下面示例中的InnerClass1
)。 意思是,只有啟用InnerClass1
默認構造函數時,才能“注釋掉” m_innerClass1(a)
和m_innerClass1(15)
。
對於每個具有默認構造函數的“組合對象” - 您可以在初始化列表中對其進行初始化,但如果您選擇不這樣做,它也可以工作(請參閱下面示例中的InnerClass2
)。
請參閱示例代碼(在Ubuntu 18.04 (Bionic Beaver) 下編譯, g++
版本為 7.3.0):
#include <iostream>
using namespace std;
class InnerClass1
{
public:
InnerClass1(int a) : m_a(a)
{
cout << "InnerClass1::InnerClass1 - set m_a:" << m_a << endl;
}
/* No default constructor
InnerClass1() : m_a(15)
{
cout << "InnerClass1::InnerClass1() - set m_a:" << m_a << endl;
}
*/
~InnerClass1()
{
cout << "InnerClass1::~InnerClass1" << endl;
}
private:
int m_a;
};
class InnerClass2
{
public:
InnerClass2(int a) : m_a(a)
{
cout << "InnerClass2::InnerClass2 - set m_a:" << m_a << endl;
}
InnerClass2() : m_a(15)
{
cout << "InnerClass2::InnerClass2() - set m_a:" << m_a << endl;
}
~InnerClass2()
{
cout << "InnerClass2::~InnerClass2" << endl;
}
private:
int m_a;
};
class MyClass
{
public:
MyClass(int a, int b) : m_innerClass1(a), /* m_innerClass2(a),*/ m_b(b)
{
cout << "MyClass::MyClass(int b) - set m_b to:" << m_b << endl;
}
MyClass() : m_innerClass1(15), /*m_innerClass2(15),*/ m_b(17)
{
cout << "MyClass::MyClass() - m_b:" << m_b << endl;
}
~MyClass()
{
cout << "MyClass::~MyClass" << endl;
}
private:
InnerClass1 m_innerClass1;
InnerClass2 m_innerClass2;
int m_b;
};
int main(int argc, char** argv)
{
cout << "main - start" << endl;
MyClass obj;
cout << "main - end" << endl;
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.