Initializing private member variables of a class - c++

I apologize in advance because some of my verbiage may not be 100% correct.
I will have a class like this:
class ClassName {
private:
AnotherClass class2;
public:
ClassName();
~ClassName();
...
In the constructor of this class, among other things, I put the line
ClassName::ClassName() {
AnotherClass class2;
}
Which is how I thought you were supposed to initialize objects in C++, however I was noticing (through GDB) that two AnotherClass objects were being created. Once on the Constructor definition then again on my initialization line. What is the reasoning behind this? What if I wanted to use a more complicated constructor like AnotherClass(int a, int b), would it create a temporary object then create the correct one shortly after?

AnotherClass class2; creates another local object inside the constructor body, that gets destroyed at the end of the body. That is not how class members are initialized.
Class members are initialized before the constructor body in the member initializer list between the constructor signature and body, starting with a :, like so:
ClassName::ClassName() :
class2(argumentsToPassToClass2Constructor),
anotherMember(42) // just for example
{
/* constructor body, usually empty */
}
If you don't want to pass any arguments to the class2 constructor you don't have to put it in the initializer list. Then its default constructor will be called.
If you simply want to call the default constructor on all of your class members, you can (and should) omit the constructor altogether. The implicitly generated default constructor will do just what you wanted.

What you are doing in your constructor is creating another variable, local only inside the constructor.
Actually, if you do nothing, the default constructor in AnotherClass will be called for the class2 object.
If you want to be explicit, you can use a constructor initializer list:
ClassName::ClassName()
: class2()
{
}
This last method is also the way you call a specific constructor with arguments in AnotherClass, if you need to do that.

ClassName::ClassName() {
AnotherClass class2; // this will create local variable only
}
If AnotherClass will have default constructor, then it will be called for the class2 object by compiler.
If you want to call parametrized constructor then you will have do it in following way:
ClassName::ClassName() :
class2(arguments)
Why to use and How to use initializer list :
Consider the following example:
// Without Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};
Here compiler follows following steps to create an object of type MyClass
Type’s constructor is called first for “a”.
The assignment operator of “Type” is called inside body of MyClass() constructor to assign
variable = a;
And then finally destructor of “Type” is called for “a” since it goes out of scope.
Now consider the same code with MyClass() constructor with Initializer List
// With Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};
With the Initializer List, following steps are followed by compiler:
Copy constructor of “Type” class is called to initialize : variable(a). The arguments in initializer list are used to copy construct “variable” directly.
Destructor of “Type” is called for “a” since it goes out of scope.
As we can see from this example if we use assignment inside constructor body there are three function calls: constructor + destructor + one addition assignment operator call. And if we use Initializer List there are only two function calls: copy constructor + destructor call.
This assignment penalty will be much more in “real” applications where there will be many such variables.
Few more scenarios, where you will have to use initializer list only:
Parametrized constructor of base class can only be called using Initializer List.
For initialization of reference members
For initialization of non-static const data members

You are just creating a local variable in this line. In general there are three ways of initializing private members:
Default initialization
If you do nothing on your constructor, the compiler will automatically initialize the private member by calling its default constructor (ctr without parameters)
Assigning them to a value in the ctr body
In this case you have to assign the desired value to your private member by using the assignment operator.
ClassName::ClassName()
{
class2 = AnotherClass(a, b, c); // if the class ctr has some parameters
}
By using the initialization list
In your case it will be something like:
ClassName::ClassName()
: class2(initial_value)
{
}
This is in general the best and efficient option for initializing your class private members since you avoid calling the copy constructor for the passed parameters. This is in general is not an issue unless the copy ctr contains time-consuming operations. The same apply for the option #2 in this case you may have the same issues with the assignment operator

What you did there is to create a new variable with the same name as you member,
By doing this you overshadowed your member variable.
Also, in the process your member constructor was silently called in the ClassName empty initialisation list.
you can initiate the class in two ways:
ClassName::ClassName(): class2() {}
or:
ClassName::ClassName() {
this->class2 = AnotherClass();
}
The first way is better and a must some times.
If you only use empty constructors for your members you won't see the difference, except in performance, because the compiler initialize the member by default in its initialisation list ( the part after the ":", if you don't do that, he does it silently for you... )
But if your member doesn't have an empty constructor, for example:
AnotherClass:: AnotherClass(int a, int b)
if you will try to use the second way on initialisation you will get a message like:
error: constructor for 'Initiator' must explicitly initialize the member 'class2' which does not have a default constructor

Related

Initialize array of objects in constructor

I wrote a class that holds an array of objects, which each expect a pointer to a c-struct in their constructor:
Here's some code:
class TheOtherClass
{
private:
SomeCStruct* m_pCStruct;
int m_ObjIdx;
public:
TheOtherClass::TheOtherClass(SomeCStruct* pCStruct, int ClassIdx)
: m_pCStruct(pCStruct),
m_ObjIdx(ClassIdx)
{}
}
class MyClass
{
private:
TheOtherClass m_objects[4];
SomeCStruct* m_pMyCStruct;
public:
MyClass::MyClass(SomeCStruct* pCStruct)
: m_pMyCStruct(pCStruct)
{
m_objects[4] = {TheOtherClass(&pCStruct, 1), TheOtherClass(&pCStruct, 2),
TheOtherClass(&pCStruct,3), TheOtherClass(&pCStruct, 4)};
}
I'd actually like to initialize the member objects of MyClass in the init list of the constructor but I was reading this isn't possible?
Hence I tried the above approach but I'm always getting the error: "TheOtherClass": no appropriate default constructor available.
Another thing to mention is that the compiler I'm running is a state of the art C++03 compiler ;-) (it's code for a DSP and TI sort of doesn't wants to update the compiler)
Any help would be highly appreciated!
Since MyClass(SomeCStruct* pCStruct) constructor doesn't (can't) initialize the TheOtherClass m_objects in the member initializer list, the m_objects will need to be default constructed at first, and then this member will be reassigned with new value in the body of the MyClass constructor.
The TheOtherClass won't have a synthesized constructor since you have defined (other, non-default) ctor that takes SomeCStruct* and int. Thus, no default constructor for it in your code.
Here, we define a default constructor:
class TheOtherClass {
SomeCStruct* m_pCStruct;
int m_ObjIdx;
public:
TheOtherClass() : m_pCStruct(nullptr), m_ObjIdx(0) {} // Default ctor
TheOtherClass(SomeCStruct* pCStruct, int ClassIdx)
: m_pCStruct(pCStruct),
m_ObjIdx(ClassIdx) {}
};
As to the MyClass. Since you've already pass the argument as a pointer MyClass(SomeCStruct* pCStruct), you don't need to take the address of it (as in &pCStruct).
And the main issue in the MyClass is that the m_objects is of class type TheOtherClass which does not define any operator=, including the one that takes brace-enclosed initializer list. So, you won't be able to do like so:
m_objects[4] = { // The operator '=' has not been defined for the TheOtherClass, also note the subscript
TheOtherClass(pCStruct, 1),
TheOtherClass(pCStruct, 2),
TheOtherClass(pCStruct,3),
TheOtherClass(pCStruct, 4)
};
Again, when you try to list initialize it, it has already been default constructed. Thus, for this approach to work you will need to define such copy-assignment operator to do this kind of assignment to an already default constructed instance of the TheOtherClass. As to how to implement this operator, it will mostly depend on the overall design and other factors which were not mentioned in this thread.
Update
If you will have the following body of the MyClass constructor, it will work:
MyClass(SomeCStruct* pCStruct) : m_pMyCStruct(pCStruct) {
// Assign to default initialized TheOtherClass object
m_objects[0] = TheOtherClass(pCStruct, 1);
}
I would advise you to check these:
Operator overloading
Constructors and member initializer lists

C++ a default constructor of a class calling another default constructor of another class

I have 2 classes Class A and B. I am trying to use Class B's default constructor to call class A's default constructor to intialize the values of class A in class B.
class A
{
A();
int x;
}
A::A()
{
//initialized x
x=10;
}
class B
{
B();
A aobj;
}
B::B()
{
//Calling class A's default constructor to initialize B's aobj.
aobj();
}
I received a no match call to '(aobj)'. Please help me to resolve.
Actually, you don't need to explicitly default construct members as that happens automatically unless you explicitly construct the member otherwise. In case you want to really construct a member explicitly, whether it is default construction or something else, you'll need to put your initialization into the member initializer list:
B::B()
: aobj() {
}
The expression aobj() in the body of a function tries to use the function call operator on the member aobj. Doing so may be reasonable, e.g., when aobj is of type std::function<void()>.
In the context of a statement, aobj() does not try to construct the aobj variable, rather it attempts to call it using the operator() operator overload.
Instead, try doing the construction in B::B()'s initializer list:
B::B() : aobj()
{
}
But note that this is redundant, since the default constructor for member objects will be called implicitly if omitted from the initializer list. That is, this constructor would do the exact same thing:
B::B() { }

Inherited class initializing a custom made class using non-default constructor

So I have searched all over the place and I can not seem to find the answer to this specific question. I am using a winXP with cygwin and gcc 3.4.4 cygming special.
Problem:
I have a class that works as an interface with some abstract methods and protected variables that should be in every class that inherits from this class. Now I also have another class that is a member variable of this interface.
class Bar {
private:
int y;
public:
Bar(int why);
};
Bar::Bar(int why) : y(why) {}
class Foo {
protected:
Bar b;
public:
Foo(int x);
virtual void print_base();
};
Foo::Foo(int x) : b(x+3) // Have to use initializer list here.
{
//this->b(x+3); // doesn't work
}
class DerFoo : public Foo {
protected:
Bar db;
public:
DerFoo(int x);
};
DerFoo::DerFoo(int x) : Foo(x),
db(x+3) // (Bar)(int) being called, works fine
// db(4.0, 30) // no matching function for call to Bar::Bar(double, int)
// note: candidates are Bar::Bar(const Bar&), Bar::Bar(int)
// b(x-3) // doesn't work class DerFoo does not have any field named 'b'
{
//this->b(x - 3); // Doesn't work, error no match for call to (Bar)(int)
//this->db(x + 3); // Doesn't work, error no match for call to (Bar)(int)
}
So the problem as you can see is inside the derived foo class, DerFoo how to initialize b. I have tried member initialization method, but then the compiler doesn't realize about protected variables. So then for some bizarre reason unbeknownst to me, it can not find the constructor in this class. Even though if were to include a "wrong" call to the constructor of a protected member variable (non inherited) it would suggest the correct version of the constructor.
I have no clue still how to do this. Any help is greatly appreciated.
After you declare a variable you have to set it otherwise you will be calling it like a function.
this->b = Bar(x+3);
The preferred way is to use the initializer list to avoid unnecessary copies of Bar. If you do, however, need to set b outside of the constructor the above example is how to do so.
DerFoo's constructors must not (and cannot) initialize b, that's Foo job. DerFoo's constructors are responsible for initializing only DerFoo's immediate subobjects, namely db and the Foo which is a base class of DerFoo. Foo's constructor, in turn, is responsible for initializing b.
The sequence of events is like:
DerFoo's constructor invokes Foo's constructor
Foo's constructor invokes b's constructor
Foo's constructor runs its body, leaving the Foo object completely constructed
DerFoo's constructor invoke's db's constructor
DerFoo's constructor runs its body, leaving the DerFoo object completely constructed.
If, in the DerFoo constructor, you don't like the value that the Foo constructor left in b, you can assign a new value to b, using any one of these syntaxes:
b = Bar(47);
this->b = Bar(47);
this->Foo::b = Bar(47);
Foo::b = Bar(47);
I don't find the question very clear, but let's see if I understood what you are trying to do and how.
DerFoo::DerFoo(int x) : Foo(x), [a]
db(x+3)
// db(4.0,30) [1]
// note: candidates are Bar::Bar(const Bar&), Bar::Bar(int)
// b(x-3) [2]
{
//this->b(x - 3); [3]
//this->db(x + 3); [4]
}
The first error is [1], where the compiler is telling you that there is no constructor of Bar that takes both a double and an int. The error also lists the two possible constructors that you can use: Bar(int), Bar(Bar const &). I am unsure as of what you intended with this line, but you have already figured out (previous line) that by just providing an int the call will work.
[2] b is not a member of DerFoo, and thus cannot be initialized in the initializer list of DerFoo. It is the responsibility of Foo to initialize it's own member and that will happen through the call to the Foo constructor in [a].
[3],[4], both expressions take the form this->member(i). During initialization the syntax member(i) will well, initialize member with the value of i. Outside of initialization the syntax means call operator()( int ) passing the value of i. Those members have already been initialized, but if you want to reset them you need to assign rather than initialize them.
b is inside the Foo class. to access it (for sure) use
Foo::b = Bar(x-3);
You don't have to use an initializer list, you definitely also should use an initializer list at this point.
At construction of an object, before the code of the constructor is entered all member variable are already constructed. If you don't give initializers, they will be default constructed.
Also you cannot construct a variable again, after it already has been constructed. Your
this->b(x+3)
is not telling the compile to construct b it is telling it to call a function named b on your object. Such a function does not exist in your class, hence the error. Note that once an object has been constructed into a variable, there is no way, to ever call the constructor for that variable again (only to change the value).
A value can be changed using = as in most languages. Hence you could do:
Foo::Foo(int x)
{
this->b = Bar(x+3);
}
This means you are creating another nameless Bar object and assigning it's value to this->b. You should be aware, that this means, you will create two Bar objects, when creating a Foo. First the default constructed one, before. The constructor code is entered, then the new nameless one. Then you will finally assign the value to the already constructed object, so this code is much more inefficient than the one, which uses initializer lists.
EDIT:
Since I missed the second doesn't work in the code above here some additional information:
You are also trying to initialize b directly in the constructor of the derived DerFoo object. However once this part of code is reached, it has already been constructed. Hence any attempt to construct it in the derived constructor comes to late.
Hence you either have to add another constructor to Foo which takes the value and use that one in your constructor of DerFoo. This solution is preferable, since it will only construct the Bar object in b once. If you cannot add such a constructor, then you need to use an assignment in the constructor code for DerFoo.
Trying to initialize b directly in the DerFoo constructor will not work even if the scoping operator is used.
DerFoo::DerFoo() : Foo::b(x-3) {}
will still produce an error: http://ideone.com/6H8ZD

Execute checks before initialization list

I have a member of class A in my own class which constructor takes multiple parameters. Im forwarding parameters of my own class to the constructor of class A. But its important that these parameters are correct, so i need to check them before consructing the member of A. And heres the problem:
I could leave out the member in the member intialization list, effectively calling the default constructor. After the checks in the constructor i could then call A`s constructor in a assigment. Although, this produces a error since the destructor of A is private.
How do i solve this?
MyClass::MyClass(int someParam) : otherMember(2){
//checks for someParam
member = A(someParam); // <- produces error
}
You're going to need an accessible destructor no matter what you do. But to address your question, one option would be to call a static function to check parameters from within the initializer:
class MyClass {
private:
static void checkParam(int);
// ...
};
MyClass::MyClass(int someParam) : otherMember( (checkParam(someParam), 2) ) {
// ...
}
static void MyClass::checkParam(int someParam) {
if (...) throw someException();
}
Note that the , used there is the comma operator, not an argument separator - it evaluates both left and right expressions, and throws away the result of the left.
I see two ways of approaching this:
Make sure class A can be used with a parameter-less constructor, and set someParam in a separate method: A.SetSomeParam(someParam)
Not inherit from A, but rather hold a member object of type A, and then you can construct it whenever you like.

Static initialization of a struct with class members

I have a struct that's defined with a large number of vanilla char* pointers, but also an object member. When I try to statically initialize such a struct, I get a compiler error.
typedef struct
{
const char* pszA;
// ... snip ...
const char* pszZ;
SomeObject obj;
} example_struct;
// I only want to assign the first few members, the rest should be default
example_struct ex = { "a", "b" };
SomeObject has a public default constructor with no arguments, so I didn't think this would be a problem. But when I try to compile this (using VS), I get the following error:
error C2248: 'SomeObject::SomeObject' : cannot access private member declared in class 'SomeObject'
Any idea why?
Update: Here's the definition of SomeObject
class SomeObject
{
void operator=(const SomeObject&);
SomeObject(const SomeObject&);
public:
SomeObject()
{
// etc
}
// members snipped
}
Your initialization of ex performs copy-initialization. It takes the value on the right and uses it to initialize the variable on the left. For class-type members, the appropriate constructor is used. In your case, that means invoking the copy constructor for SomeObject, but you've made that constructor private, so the compiler is correct in telling you that SomeObject::SomeObject is a private member that can't be accessed.
Although the compiler is allowed to elide the call to the copy constructor and initialize ex.obj directly with the default constructor, that is an optional optimization; it still needs to be allowed to call the copy constructor.
You can either give example_struct a constructor of your own and use that in place of brace initialization, or you can publicize SomeObject's copy constructor.
The rest of the members will be initialized to '0', not "default", right? So it's probably trying to call SomeObject(0), which I'd guess is resolving to a private copy constructor.
It looks like the constructor for SomeObject is private.