I have a member variable that is a class that prohibits copying (copy assignment operator is deleted). I want to make some checks to determine what to initiate the member to, thus I need to do this inside the constructor and not in and initializer list. It seem the member variable m is already initialed with the default constructor before entering the constructor of MyClass, then what is the point of the constructor... sorry for c++ rant.
Simple example:
class MyClass {
NonCopy m;
MyClass() {
// Complex checks
if(success) {
m("success");
else {
m("failure");
}
}
The options I see is:
Reside to dynamic allocation of m
Relax copy prohibit requirement of m
As long as the class has a valid copy or move constructor, or you are using C++17+, you can make a helper function that does the logic and then returns the correct object. You then use that to initialize your member. That would look like
class MyClass {
NonCopy m;
static NonCopy initialize_noncopy()
{
// Complex checks
if(success) {
return NonCopy("success");
else {
return NonCopy("faulure");
}
}
public:
MyClass() : m(initialize_noncopy()) { }
};
Well, you're supposed to initialize member variables before the constructor body
struct C {
M member;
C() : member("some arguments") { }
};
But this only works if you know the arguments for the member's initializer at that time! If you really don't want to initialize member until you get some more information in the constructor body, you can place it into a single-member anonymous union.
struct C {
union { M member; };
C() { // member is not initialized
// do whatever
new(&member) M("some arguments"); // explicitly initialize
}
// now you have to write all the other special functions!
~C() { member.~M(); }
C(C const &x) = delete;
C(C &&x) { new(&member) M(std::move(x.member)); }
C &operator=(C const &x) = delete;
C &operator=(C &&x) { member = std::move(x.member); return *this; }
};
Related
in C++, what is the difference between initialization list and assigning values in a constructer rather than the way each method looks?
I mean what's the advantage of using one rather than the other and why in the given example in the slide (below) only works with initialization? (I hope if you could add some resources to it since I didn't find)
Click here to view slide: uploaded on imgur
Using initialization list in constructor is the one step process i.e. it initializes objects at the moment it’s declared. It calls copy constructor.
Whereas using assignment is the two-step process i.e. define the object and then assign it. Defining objects calls default constructor and then assignment calls assignment operator. Hence, expensive operations.
In C++, constant or reference data member variables of a class can only be initialized in the initialization list, not using assignment in constructor body.
Both constant and reference data member variables have property that they both must be initialized at the moment of declaration. So, there is only way to use initialization list in constructor, as initialization list initializes class member variables at the time of declaration whereas assignment if constructor body initializes data members after declaration.
There are situations where initialization of data members inside constructor doesn’t work and Initializer List must be used. Following are such cases.
For initialization of non-static const data members.
#include<iostream>
using namespace std;
class Test {
const int t;
public:
Test(int t):t(t) {} //Initializer list must be used
int getT() { return t; }
};
int main() {
Test t1(10);
cout<<t1.getT();
return 0;
}
For initialization of reference members.
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test(int &t):t(t) {} //Initializer list must be used
int getT() { return t; }
};
int main() {
int x = 20;
Test t1(x);
cout<<t1.getT()<<endl;
x = 30;
cout<<t1.getT()<<endl;
return 0;
}
For initialization of member objects which do not have default constructor. (In your case Array digits does not have default constructor)
#include <iostream>
using namespace std;
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
// Class B contains object of A
class B {
A a;
public:
B(int );
};
B::B(int x):a(x) { //Initializer list must be used
cout << "B's Constructor called";
}
int main() {
B obj(10);
return 0;
}
For initialization of base class members.
When constructor’s parameter name is same as data member.
For Performance reasons.
If you don't use an initialization list, the data members of the class will be default constructed before the body of the constructor is reached:
class Foo{
private:
int bar;
public:
Foo(int _bar){//bar is default constructed here
bar = _bar; //bar is assigned a new value here
}
};
This isn't a big issue for a fundamental-type, like int, as the default constructor is not expensive. However, it can become an issue if the data member does not have a default constructor, or default construction followed by assignment is more expensive than direct construction:
//Bar does not have a default constructor, only a copy constructor
class Bar{
public:
Bar() = delete; //no default constructor
Bar(const Bar& bar); //copy constructor only
};
class Foo{
private:
Bar bar;
public:
Foo(const Bar& _bar){
//this will not compile, bar does not have a default constructor
// or a copy assignment operator
bar = _bar;
}
Foo(const Bar& _bar) : bar(_bar){//this will compile, copy constructor for bar called
}
};
Generally speaking, use the initialization list for more efficient code.
Consider I want to wrap some library code inside an object. That library needs to be set up and initialized by calling some function inside the constructor of that wrapper class.
The librarie's "objects" then diverge into creating more, different "objects" that the wrapper class wraps in form of yet another wrapper object that should be a plain member of that class.
But as far as I see it, members of classes can only be initialized or created by calling their constructor in the initalizer list of the constructor. The execution of these bits of code preceed the constructor of the actual class that does the initialization of the library and its environment, making it impossible for me to actually initialize that member object as a member and instead force me to initialize it as a pointer to the 2nd wrapper, because its constructor must be called manually within the first constructor's code.
Example:
class A {
public:
A() {
if(!wrapped_library_init()) {
exit(CRITICAL_ERROR);
}
ptr_to_some_library_metadata *a = library_function(); /*Needs to
be called after wrapped_library_init() or needs a pointer to some
wrapped object created inside this constructor */
//initialize b
}
private:
B b; //Wants to be a member but can not
};
class B {
B(ptr_to_some_library_metadata *a);
}
Member objects can only be constructed in the member initializer list. There are a few techniques which can be used to make it possible to initialize an object, though:
Use a helper [lambda] function doing the necessary extra work before return a suitable object. For example:
A()
: B([]{
if (!wrapped_library_init()) {
exit(CRITICAL_ERROR);
}
return library_function();
}()) {
}
You can delay construction by using a union with just the appropriate member. When using this technique the member needs to be explicitly destructed, for example:
class A {
union Bu { B b };
Bu b;
public:
A() {
if (!wrapped_library_init()) {
exit(CRITICAL_ERROR);
}
new(&b.b) B(library_function());
}
~A() {
b.b.~B();
}
// ...
};
I'd personally use the first approach. However, there are cases when using a union to delay construction is helpful.
Initializer lists are there to use another constructor than the default constructor.
But nothing impedes you for creating a custom function that will initialize b:
class A {
public:
A():b(init()) {
}
private:
B b; //Wants to be a member but can not
static B init()
{
if(!wrapped_library_init()) {
exit(CRITICAL_ERROR);
}
ptr_to_some_library_metadata *a = library_function(); /*Needs to
be called after wrapped_library_init() or needs a pointer to some
wrapped object created inside this constructor */
return B(a);
}
};
Wrap your library inside a class:
class LibraryWrapper
{
public:
LibraryWrapper()
{
if(!wrapped_library_init()) {
exit(CRITICAL_ERROR);
}
lib_data.reset(library_function()); /*Needs to
be called after wrapped_library_init() or needs a pointer to some
wrapped object created inside this constructor */
}
//~LibraryWrapper() {/**/}
//LibraryWrapper(const LibraryWrapper&) {/**/} // =delete; ?
//LibraryWrapper& operator=(const LibraryWrapper&) {/**/} // =delete; ?
//private: // and appropriate interface to hide internal
std::unique_ptr<ptr_to_some_library_metadata, CustomDeleter> lib_data;
};
class B {
public:
explicit B(ptr_to_some_library_metadata *a);
// ...
};
And use extra member or inheritance:
class A
{
public:
A() : b(lib.lib_data.get()) {}
private:
LibraryWrapper lib; // placed before B
B b;
};
I have observed that the following code segfaults at the line ar.p():
#include <iostream>
class A
{
public:
virtual void p() { std::cout<<"A!\n"; }
};
class B : public A
{
public:
void p() { std::cout<<"B!\n"; }
};
struct Param
{
enum {AA, BB} tag;
union {
A a;
B b;
};
Param(const A &p)
: tag(AA) {a = p;}
A &get() {
switch(tag) {
case AA: return a;
case BB: return b;
}
}
};
int main() {
A a;
a.p();
Param u(a);
A &ar = u.get();
ar.p();
}
However, when I change the Param constructor to:
Param(const A &p)
: tag(AA), a(p) {}
it does not segfault anymore.
I think it has something to do with the way the vtable ptr for union member a is initialized, but I'd like to understand this bug better.
On coliru: http://coliru.stacked-crooked.com/a/85182239c9f033c1
The union doesn't have an implicit constructor, you have to add your own constructor to the union which initializes one of the members of the union. I think this is because the compiler can't know whether you want to initialize a or b. You may also need an assignment operator and destructor. See also this question: Why does union has deleted default constructor if one of its member doesn't have one whatsoever?
The constructor should use placement new or it can use a member initializer to construct one of the union members, as you do in your alternative constructor.
If you want to assign something to b afterwards, you have to destruct a using a.~A(), and then initialize b with placement new.
If you have members in the union with a non-trivial destructor then the union must have a destructor which calls the destructor on the member which is used at that point.
In your original code the assignment operator and the p() method are called without running a constructor first, leading to the crash.
Previously I had:
MyClass::MyClass() : myMember(VALUE,this,5,"etc.") { ... }
but I need to do some calculation of the values passed to myMember's constructor. How do I initialise myMember within the constructor body instead? I'm not sure if I should do it like this:
MyMember myMember(calculatedValue,this,5,"etc.");
All member initialisation occurs before the constructor body runs. So if you did this:
MyClass::MyClass()
{
myMember = MyMember(calculatedValue,this,5,"etc.");
}
it would be assignment, not initialisation. So it won't work if MyMembers are not assignable (e.g. they have a private assignment operator). It also won't work if myMember is declared as const.
You could, however, do something like this:
class MyClass {
public:
MyClass() : myMember(calculate(VALUE),this,5,"etc.") {}
private:
static int calculate(int x) { return x*3; }
};
or even something like this:
class MyClass {
public:
MyClass() : myMember(createMember()) {}
private:
static MyMember createMember(int x) {
MyMember m;
m.foo = 5;
...
return m;
}
};
Note that I've used static helper functions here, because using non-static member functions before the constructor has completed gets a little weird, in the general case.
If you assign the myMember member inside MyClass' constructor, you are really invoking MyMember's default costructor, then invoking the assignment operator.
If it's fine for you, then you just need to write:
MyClass::MyClass()
{
myMember = MyMember(calculatedValue,this,5,"etc.");
}
otherwise, you need a different approach.
Let's say that I have two classes A and B.
class A
{
private:
int value;
public:
A(int v)
{
value = v;
}
};
class B
{
private:
A value;
public:
B()
{
// Here's my problem
}
}
I guess it's something basic but I don't know how to call A's constructor.
Also the compiler demands a default constructor for class A. But if A has a default constructor than wouldn't the default constructor be called whenever I declare a variable of type A. Can I still call a constructor after the default constructor has been called? Or can I declare an instance of a class and then call a constructor later?
I think this could be solved using pointers but can that be avoided ?
I know that you can do something like this in C#.
EDIT: I want to do some computations in the constructor and than initialize the classes. I don't know the values beforehand.
Let's say I'm reading the value from a file and then I initialize A accordingly.
The term you are looking for is initializer list. It is separated from the constructor body with a colon and determines how the members are initialized. Always prefer initialization to assignment when possible.
class A
{
int value;
public:
A(int value) : value(value) {}
};
class B
{
A a;
public:
B(int value) : a(value) {}
}
I want to do some computations in the constructor and than initialize the classes. I don't know the values beforehand.
Simply perform the computation in a separate function which you then call in the initializer list.
class B
{
A a;
static int some_computation()
{
return 42;
}
public:
B() : a(some_computation()) {}
}
You use an initialization list to initialize the member variables.
public:
B() : value(4) { // calls A::A(4) for value.
}
Or can I declare an instance of a class and then call a constructor later?
Yes, you can do that. Instead of (A value;) declare (A* value;), and then B's constructor will be B():value(new A(5)){}.
In the B's destructor you will have to do delete value;
I think this could be solved using pointers but can that be avoided ?
Yes. Use shared_ptr.
Try:
B() :
value(0)
{
// Here's my problem
}