brace-or-equal initialization vs constructor delegation - c++

Perhaps my title might translate to "apples vs oranges", but I'm not quite sure so I'd like the SO community's help understanding some best practices.
Suppose I have the two examples.
brace-or-equal initialization
class foo
{
public:
foo() {}
foo(int a) : m_a(a) {}
foo(int a, int b) : m_a(a), m_b(b) {}
foo(int a, int b, int c) : m_a(a), m_b(b), m_c(c) {}
private:
int m_a = 1;
int m_b = 2;
int m_c = 3;
};
constructor delegation
class foo
{
public:
foo() : m_a(1), m_b(2), m_c(3) {}
foo(int a) : foo() { m_a = a; }
foo(int a, int b) : foo() { m_a = a; m_b = b; }
foo(int a, int b, int c) : foo() { m_a = a; m_b = b; m_c = c; }
private:
int m_a;
int m_b;
int m_c;
};
I feel like the first is better because it results in no double-initialization of variables. In the 2nd example, the last overload of foo's constructor is the worst because each member variable is initialized twice.
Which is the preferred method here? And assuming the former is preferred, when are delegated constructors useful?

You want to delegate from the constructor that takes fewer arguments to the constructor that takes the most, giving default values for the extra parameters:
foo( int a, int b, int c ) : m_a(a), m_b(b), m_c(c) {}
foo( int a, int b ) : foo( a, b, 3 ) {}
foo( int a ) : foo( a, 2, 3 ) {}
foo() : foo( 1, 2, 3 ) {}

Like clcto pointed out, you should do delegation differently.
In reality we can do a little better (With your given example!!!) using default param values:
class foo
{
public:
// no longer need all your other constructors
foo(int a=1, int b=2, int c=3) : m_a(a),m_b(b),m_c(c){}
private:
int m_a;
int m_b;
int m_c;
};
Your actual code may be different; i.e. you want to sometimes initialize b and not a or c, so you could instead use one of the other answers posted.

Related

How does constructor inheritance work in C++?

According to my research, C++ does not automatically inherit constructors in subclasses, so given the code below:
struct foo
{
int a;
int b;
foo(int a, int b) : a(a), b(b) {}
foo() : a(0), b(0) {}
};
struct bar : foo{};
struct baz
{
int a;
baz(int a) : a(a) {}
baz() : a(0) {}
};
struct qux : baz{};
We should not be able to create an object baz in the same way as foo without first redefining the constructor.
int main(){
bar b(1,2);
std::cout << b.a;
}
gives an error as we should expect. However
int main(){
qux b(1);
std::cout << b.a;
}
returns 1, despite qux having no explicit constructor. Did it receive its constructor from baz or is this a quirk of it being a single parameter constructor, or is it something else?
Also, if I do go in and add the constructor to bar
bar(int a, int b)
{
this->a = a;
this->b = b;
}
and
bar(int a, int b) : foo(a,b) {} work, but bar(int a, int b) : a(a), b(a) {} does not. Why is this?
Thanks a bunch.
qux b(1);
works because implicit conversion takes place (from int to baz)
and then move constructor is used to create qux from baz
delete move constructor or make baz(int) constructor explicit and it will not work
struct baz
{
int a;
baz(const baz& o) = delete;
/*explicit*/ baz(int a) : a(a) {}
baz() : a(0) {}
};
as for
but bar(int a, int b) : a(a), b(a) {} does not.
it will work if you create foo and pass it to move constructor like this:
bar b({1,2});

Delegate constructor and default argument depending on other arguments

I have a constructor for B with some default argument depending on other arguments:
struct A
{
int f();
A(const A&) = delete;
A(A&& );
// ....
};
struct B
{
B(A a, int n=a.f()) {//...}
// ...
};
This clearly does not work in that way, so I want use a delegate constructor:
struct B
{
B(A a, int n) {//...}
B(A a): B(a, a.f()) {}
};
This, however, also does not work because the copy constructor of A is deleted. So I need something like
struct B
{
B(A a, int n) {//...}
B(A a): B(std::move(a), a.f()) {}
};
As far as I know, however, there is no guarantee that a.f() is evaluated before std::move, so the result is undefined. Is there a possiblity to get the value of a.f() before std::move or should I better write two seperate constructors?
Why don't you do something simpler - i.e. overload your constructor?
struct B
{
B(A a) {
int n = a.f();
...
}
B(A a, int n) {
...
}
};
If you don't like repetition of your code in ..., you can always have just a single call to a private member function that does the rest of the construction.
The are more possible solutions for this.
The most simple approach is to make a a pointer:
struct B
{
B(A* a, int n) {...}
B(A* a): B(a, a->f()) {}
};
A more complex approach is to try to make a a reference:
struct B
{
B(A& a, int n) {...}
B(A& a): B(a, a.f()) {}
};
I would not suggest this solution. The pointer is a cleaner approach.
Edit:
Via std::move from the utility libary
struct B
{
A&& a:
int n:
B(A&& a, int n): a(std::move(a)), n(n) {...}
B(A&& a): B(std::move(a), a.f()) {...}
};

Issue in accessing member variables

#include<iostream>
class A {
int a, b;
public:
void setdata(int x, int y) { a = x; b = y; }
void showdata() { std::cout << a << b; }
};
class B : public A { };
int main() {
A a1;
B b1;
a1.setdata(5, 4);
a1.showdata();
b1.showdata();
}
I just want to print the values of the a and b members using the b1 object of class B, as it can access the member functions of class A since class B has public inheritance of class A. But, I am getting garbage values when I try to print the values of a and b using b1.
Can someone explain why this is happening and how to fix it?
a1 and b1 are completely separate object instances in memory. They have their own copies of the a and b members in memory. They have nothing to do with each other at all. Whatever you do to a1 does not affect b1 at all, and vice versa.
You are initializing the members of a1 only, you are not initializing the members of b1 at all. That is why you are seeing garbage when you try to print out the members of b1.
Before calling b1.showdata(), you need to call b1.setdata() to initialize b1's members, eg:
int main() {
A a1;
B b1;
a1.setdata(5, 4);
a1.showdata();
b1.setdata(1, 2); // <-- add this!
b1.showdata();
}
You should also give class A a default constructor that initializes the members to default values, in case setdata() is not called after construction (such as what happened in your case), eg:
class A {
int a, b;
public:
A() : a(0), b(0) {} // <-- add this!
void setdata(int x, int y) { a = x; b = y; }
void showdata() { std::cout << a << b; }
};
Alternatively, you might consider giving class A and class B a constructor that takes values as input, eg:
class A {
int a, b;
public:
A() : a(0), b(0) {}
A(int x, int y) : a(x), b(y) {} // <-- add this!
void setdata(int x, int y) { a = x; b = y; }
void showdata() { std::cout << a << b; }
};
class B : public A {
public:
B() : A() {}
B(int x, int y) : A(x, y) {}
};
/* or simpler:
class B : public A {
public:
using A::A; // <-- inherit all constructors
};
*/
int main() {
A a1(5, 4);
B b1(1, 2);
a1.showdata();
b1.showdata();
}

Code Refactoring Trying to use Polymorphism and std::map

I have this class
struct B {
B( int x=0 ) { }
virtual void something() {
std::cout << "B()";
}
};
struct A {
B b;
int a;
A( int a, int b_ ) : a(a), b(b_) {a}
};
I have implemented another class C
struct C : public B {
C( int x ) : B(b) { }
virtual void something() {
std::cout << "C()";
}
};
What is the best way to get A use C instead of B ?
I already tried this :
struct A {
B & b;
int a;
A( int a, B &b_ ) : a(a), b(b_) {a}
};
But then I have to use std::map<int, A>
which is giving compilation errors:
'A::A' : no appropriate default constructor available.
so i did this :
struct A {
B & b;
int a;
A( int a=0, B &b_=B() ) : a(a), b(b_) {a}
};
in my main
std::map<int,A> mmap;
for( int i=0;i<5;++i ) {
auto & b = C();
mmap.insert( std::make_pair(i,A(i,b) ) ) ;
}
but these C's object in mmap's A become B as soon as auto & b goes out of scope.
Its not working. How to fix it ?
In the A constructor, you try to assign a temporary instance of B to reference to B, which is illegal.
What you have to do is to change this reference by a pointer to this class :
#include <iostream>
#include <memory>
#include <map>
using namespace std;
struct B {
B( int x=0 ) { }
virtual void something()
{
std::cout << "B()";
}
};
struct A {
shared_ptr<B> b;
int a;
A( int a = 0, shared_ptr<B> b = shared_ptr<B>(new B()) ) : a(a), b(b) {}
};
struct C : public B {
C( int x ) { }
virtual void something()
{
std::cout << "C()";
}
};
int main()
{
shared_ptr<B> b = shared_ptr<B>( new C(0));
// Polymorphic test
b->something();
A a (0, b);
// map test
map<int, A> my_map;
my_map[0] = a;
return 0;
};
If you can not use C++11, just change smart pointers by naked pointers and handle the dynamic memory properly as usual. Or even better : use the Boost library, as mentioned by chris.

Calling another constructor when constructing an object with const members

I have a class with const members, and one constructor which calls another constructor with extra values filled in. Normally I could use a colon initializer for this, but the function is complex (printf/sprintf-like) and requires me to use a variable on the stack, so I have to do this in the body of the constructor and use assign *this to the new object. But of course this is invalid, because my member variables are const.
class A
{
public:
A(int b) : b(b), c(0), d(0) // required because const
{
int newC = 0;
int newD = 0;
myfunc(b, &newC, &newD);
*this = A(b, newC, newD); // invalid because members are const
// "cannot define the implicit default assignment operator for 'A', because non-static const member 'b' can't use default assignment operator"
// or, sometimes,
// "error: overload resolution selected implicitly-deleted copy assignment operator"
};
A(int b, int c, int d) : b(b), c(c), d(d) { };
const int b;
const int c;
const int d;
};
A a(0);
(I haven't explicitly deleted the assignment operator.) I declared the members const because I would like them to be public, but not mutable.
Is there some canonical way of solving this problem without using scary casts and force-overriding the members' constness? What's the best solution here?
You can add a parameters class and use either C++11 constructor delegation or a base class:
struct parameters {
int b; int c; int d;
parameters(int b): b(b), c(), d() {
myfunc(b, &c, &d);
}
};
// constructor delegation
class A {
public:
A(int b): A(parameters(b)) { }
A(parameters p): b(p.b), c(p.c), d(p.d) { }
};
// base/wrapper
class ABase {
ABase(parameters p): b(p.b), c(p.c), d(p.d) { }
};
class A: public ABase {
public:
A(int b): ABase(parameters(b)) { }
};
How about making a helper function:
class A
{
static int initializor(int b) { int n; myfunc(b, &n); return n; }
public:
explicit A(int b_) : b(b_), c(initializor(b_)) { }
A(int b_, int c_) : b(b_), c(c_) { }
// ... as before ...
};
I prefer Kerrek SB's answer, but in your case there is the complication that you can't easily make separate initialisation functions for each member.
In that case, another solution is to move the members to a base class and initialize that base class with a helper class with non-const members. Your initialization code is moved to the helper class' constructors, and can assign without problems.
class A_init
{
public:
A_init(int b)
{
// do whatever you like with c and d:
c = ...;
d = ...;
}
int c; // Note: non-const
int d; // Note: non-const
};
class A_base
{
public:
A_base(int b, A_init init) : b(b), c(init.c), d(init.d) {}
A_base(int b, int c, int d) : b(b), c(c), d(d) {}
const int b;
const int c;
const int d;
};
class A : public A_base
{
public:
A(int b) : A_base(b, A_init(b)) {}
A(int b, int c, int d) : A_base(b, c, d) {}
};
If one wants restrict access to A_init, one can switch to private and declare A a friend.
Where to put the results of myfunc so it can be set and used from different mem-initializers? How about in a default argument?
class A
{
private:
struct InitData;
public:
A(int b, InitData data=InitData());
A(int b, int c, int d) : b(b), c(c), d(d) { };
const int b;
const int c;
const int d;
};
struct A::InitData
{
int setup(int b);
int c;
int d;
};
inline int A::InitData::setup(int b)
{
myfunc(b, &c, &d);
return b;
}
inline A::A(int b_, InitData data)
: b(data.setup(b_)),
c(data.c),
d(data.d) {}
A a(0);
Since the made up type is private and has no conversions, there's little risk of accidentally using it or abusing it.