So if I have something like the following:
class MyClass{
public:
MyClass(){
// do other stuff
*oc = OtherClass(params);
}
private:
OtherClass* oc;
}
When is a constructor called on OtherClass? Would its default be called once as soon as the MyClass initialization begins, and then be redefined with its value constructor during the MyClass constructor? Or does it just not exist during "//do other stuff". What if no default constructor is provided for other class, only a value? Would it be good practice to construct it where it is defined as a member variable?
CodePudding user response:
A default constructor is one that can be called without parameters. For example this is a default constructor:
struct foo {
foo(){} // (should not actually be user defined)
};
This is also a default constructor:
struct bar {
bar(int x = 42) {}
};
In your code it might be that the constructor that is called is a default constructor, but it does not matter for your code, because you do pass a parameter.
When is a constructor called on OtherClass?
In the line *oc = OtherClass(params);
.
Would its default be called once as soon as the MyClass initialization begins, and then be redefined with its value constructor during the MyClass constructor?
If you do not provide an initializer members are default initialized. Confusingly for a pointer this means it is not initialized.
Or does it just not exist during "//do other stuff".
The member does exist before, but its value is indeterminate. You cannot use the value without invoking undefined behavior.
What if no default constructor is provided for other class, only a value?
See above. The existance of a default constructor of OtherClass
is not relavant here. It would be relevant if the member was OtherClass
and not a pointer, because for members of class type default initialization calls the default constructor.
Would it be good practice to construct it where it is defined as a member variable?
It is good practice to provide an initializer for members rather than assign in the constructor:
class MyClass{
public:
MyClass() : oc(params) {
}
private:
OtherClass oc;
}
I replaced the member with an instance rather than a pointer, because using a raw pointer as member opens up a can of worms that would require an even longer answer. For more on that read What is The Rule of Three?. Note that when the member is not a pointer but a OtherClass
then suddenly it matters if OtherClass
has a default constructor, because if you do not provide an initializer, then the member will be default constructed. Though in the above I used the member initializer list and the member will be initialized by the constructor that takes one parameter.
CodePudding user response:
ÓtherClass *oc;
is a pointer and as such has no constructor. It has to be initialized to a valid object before you can dereference it.
You can ensure oc
is initialized by, well, initializing it:
MyClass() : oc(new OtherClass()) {
...
*oc = OtherClass(params);
}
This will create a dummy oc
when the class it created and then copy or move assign the real object to *oc
later. This is wasteful, so why not initialize it with the final value directly:
MyClass() : oc(new OtherClass(params)) {
...
}
or if you have to compute params first:
MyClass : oc(nullptr) {
...
oc = new OtherClass(params);
}
Initializing oc
with nullptr
first isn't required but it is dirt cheap and it ensures accidentally using oc
will access a nullptr and fail instead of oc
being some random value that might not crash.
You can also, and better, ensure initialization by inlineing that:
class MyClass {
...
private:
OtherClass *oc(nullptr);
}
With that the compiler will initialize oc
whenever you don't initialize it in an initializer list in the constructor.
That said: Do you really need a pointer there? You need a destructor, copy/move constructors and aissgnment operators to handle the pointer directly. An Otherclass oc;
would be much easier to deal with.
But if you do need a pointer then you should use a smart pointer to handle ownership for you. That means use std::unique_ptr
or more likely std::shared_ptr
for it. You might not even need anything past the constructor that way. But think about what it means for copy/move to have the pointer shared. Read about the rule of 0/3/5.