问题
I've got a class that has a couple of objects as member variables. I don't want the constructors for these members to be called when declared, so I'm trying to hang onto a pointer to the object explicitly. I have no idea what I'm doing. o_O
On StackOverflow, I seem to be able to find other examples of object member variables, but usually the constructor is called immediately, like this:
class MyClass {
public:
MyClass(int n);
private:
AnotherClass another(100); // this constructs AnotherClass right away!
};
But I want the MyClass
constructor to call the AnotherClass
constructor. Here's what my code looks like:
BigMommaClass.h
#include "ThingOne.h"
#include "ThingTwo.h"
class BigMommaClass {
public:
BigMommaClass(int numba1, int numba2);
private:
ThingOne* ThingOne;
ThingTwo* ThingTwo;
};
BigMommaClass.cpp
#include "BigMommaClass.h"
BigMommaClass::BigMommaClass(int numba1, int numba2) {
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
}
Here's the error I'm getting when I try to compile:
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
Am I using the right approach but the wrong syntax? Or should I be coming at this from a different direction?
回答1:
You can specify how to initialize members in the member initializer list:
BigMommaClass {
BigMommaClass(int, int);
private:
ThingOne thingOne;
ThingTwo thingTwo;
};
BigMommaClass::BigMommaClass(int numba1, int numba2)
: thingOne(numba1 + numba2), thingTwo(numba1, numba2) {}
回答2:
You're trying to create a ThingOne
by using operator=
which isn't going to work (incorrect syntax). Also, you're using a class name as a variable name, that is, ThingOne* ThingOne
. Firstly, let's fix the variable names:
private:
ThingOne* t1;
ThingTwo* t2;
Since these are pointers, they must point to something. If the object hasn't been constructed yet, you'll need to do so explicitly with new in your BigMommaClass
constructor:
BigMommaClass::BigMommaClass(int n1, int n2)
{
t1 = new ThingOne(100);
t2 = new ThingTwo(n1, n2);
}
Generally initializer lists are preferred for construction however, so it will look like:
BigMommaClass::BigMommaClass(int n1, int n2)
: t1(new ThingOne(100)), t2(new ThingTwo(n1, n2))
{ }
回答3:
This question is a bit old, but here's another way in c++11 of "doing more work" in the constructor before initialising your member variables:
BigMommaClass::BigMommaClass(int numba1, int numba2)
: thingOne([](int n1, int n2){return n1+n2;}(numba1,numba2),
thingTwo(numba1, numba2) {}
The lambda function above will be invoked and the result passed to thingOnes constructor. You can of course make the lambda as complex as you like.
回答4:
I know this is 5 years later, but the replies above don't address what was wrong with your software. (Well Yuushi's does, but I didn't realise until I had typed this - doh!). They answer the question in the title How can I initialize C++ object member variables in the constructor? This is about the other questions: Am I using the right approach but the wrong syntax? Or should I be coming at this from a different direction?
Programming style is largely a matter of opinion, but an alternative view to doing as much as possible in a constructor is to keep constructors down to a bare minimum, often having a separate initialization function. There is no need to try to cram all initialization into a constructor, never mind trying to force things at times into the constructors initialization list.
So, to the point, what was wrong with your software?
private:
ThingOne* ThingOne;
ThingTwo* ThingTwo;
Note that after these lines, ThingOne
(and ThingTwo
) now have two meanings, depending on context.
Outside of BigMommaClass, ThingOne
is the class you created with #include "ThingOne.h"
Inside BigMommaClass, ThingOne
is a pointer.
That is assuming the compiler can even make sense of the lines and doesn't get stuck in a loop thinking that ThingOne
is a pointer to something which is itself a pointer to something which is a pointer to ...
Later, when you write
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
bear in mind that inside of BigMommaClass
your ThingOne
is a pointer.
If you change the declarations of the pointers to include a prefix (p)
private:
ThingOne* pThingOne;
ThingTwo* pThingTwo;
Then ThingOne
will always refer to the class and pThingOne
to the pointer.
It is then possible to rewrite
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
as
pThingOne = new ThingOne(100);
pThingTwo = new ThingTwo(numba1, numba2);
which corrects two problems: the double meaning problem, and the missing new
. (You can leave this->
if you like!)
With that in place, I can add the following lines to a c++ program of mine and it compiles nicely.
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);
};
When you wrote
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
the use of this->
tells the compiler that the left hand side ThingOne
is intended to mean the pointer. However we are inside BigMommaClass
at the time and it's not necessary.
The problem is with the right hand side of the equals where ThingOne
is intended to mean the class. So another way to rectify your problems would have been to write
this->ThingOne = new ::ThingOne(100);
this->ThingTwo = new ::ThingTwo(numba1, numba2);
or simply
ThingOne = new ::ThingOne(100);
ThingTwo = new ::ThingTwo(numba1, numba2);
using ::
to change the compiler's interpretation of the identifier.
回答5:
I'm (also, as others mentioned) aware to the fact that this question is old, yet I wanted to point something regarding the first (and great) answer from @chris who proposed a solution to the situation where the class members are held as a "true composite" members (i.e.- NOT as pointers NOR references). The note is a bit large so I will demonstrate it here with some sample code.
When you chose to hold the members as I mentioned, you have to keep in mind also these two things:
1) For every "composed object" that DOES NOT have a default ctor - you MUST initialize it in the initialization list of ALL the ctor's of the "father" class (i.e.- BigMommaClass
or MyClass
in the original examples and MyClass
in the code below), in case there are several (see InnerClass1
in the example below). Meaning, you can "comment out" the m_innerClass1(a)
and m_innerClass1(15)
ONLY if you enable the InnerClass1
default ctor.
2) For every "composed object" that DOES have a default ctor - you MAY initialize it within the initialization list, but it will work also if you chose not to (see InnerClass2
in the example below).
See sample code (complied under Ubuntu 18.04 with g++
version 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 cotr
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;
}
来源:https://stackoverflow.com/questions/12927169/how-can-i-initialize-c-object-member-variables-in-the-constructor