3

The following piece of code gets compiled under g++ 4.6.3 for Linux

#include <iostream>

class A {
  public:  

    int x;
    std::string c;

    A(int x,std::string c):x(10),c("Hi"){
    }

    ~A(){
      std::cout << "Deleting A()" << std::endl;
    }
};

class B : public A {
  public:

    B():A(20,"Hello"){
    }

    ~B(){
      std::cout << "Deleting B()" << std::endl;
    }
};

int main(){
  B o;
  std::cout << o.x << std::endl;
  std::cout << o.c << std::endl;
  return(0);
}

but it does not do what is supposed to do, the type B is not able to change the values of that 2 variables that it inherit from A.

Any explanations about why this doesn't work properly ?

axis
  • 874
  • 2
  • 7
  • 13
  • Just because you name the parameters of the constructor the same as your members it does not mean they will be copied by some sort of magic, you will have to do this by hand. – Nobody moving away from SE Oct 06 '12 at 08:10

3 Answers3

5

Your base constructor takes those values...and completely disregards them!

Change this:

A(int x,std::string c):x(10),c("Hi"){}

to this:

A(int x,std::string c):x(x),c(c){}
chris
  • 60,560
  • 13
  • 143
  • 205
  • so i need a constructor defined and i can't use initialization lists ? can you correct my example ? – axis Oct 06 '12 at 08:09
  • thanks, problem solved, i have misunderstood how initialization lists works. – axis Oct 06 '12 at 08:11
  • 1
    @axis, I provided the fix. When you call the base constructor in your derived constructor's initialization list, it picks the one with (int, std::string) parameters (the only one), which in turn ignores the arguments you give it. GCC would warn with something like: `warning: unused parameter x`, `warning: unused parameter c`. – chris Oct 06 '12 at 08:11
  • but this way i have lost the initialization list on A(), there is no way to keep the list for both ? – axis Oct 06 '12 at 08:14
  • @axis: what do you mean by initialization list? If you want A to be default constructed with `x = 10` and `c = "Hi"`, then add a default constructor: `A() : x(10), c("Hi") {}` – Nobody moving away from SE Oct 06 '12 at 08:16
  • @axis, Yes, having a ctor with 2 parameters that ignores them is a bit odd. I'd make the changes that I suggested (making the two parameter one protected if you don't things besides derived classes to call it) and change yours to a default constructor. – chris Oct 06 '12 at 08:18
  • @Nobody and this is my approach in the first place, i want to use 1 list for A and 1 list for B, initialization lists for both of them. – axis Oct 06 '12 at 08:19
  • @chris so i need to decide which one needs a constructor and can't have a list ? i can't do this things for 2 types with inheritance between each other ? – axis Oct 06 '12 at 08:20
  • @axis, Could you explain more clearly what it is you want to happen in each circumstance? – chris Oct 06 '12 at 08:21
  • @chris your fix make this work as expected in **this particular case** only, suppose that i have A defined in another file/place, B defined in another file/place, also suppose that i don't know if B will inherit from A, it's something that can happen but it's not mandatory; to keep my code much more compact i would like to initialization lists on both A and B, without being subject to my first problem with A that doesn't receive new values from the B constructor. I don't think that having a constructor defined will impact on the performance of my program, but the code will result more verbose – axis Oct 06 '12 at 08:41
  • so i would like to keep initialization list and try to avoid defined constructors just to keep my code compact. – axis Oct 06 '12 at 08:42
  • 3
    @axis why don't you use `A(int x = 10,std::string c = std::string("Hi")):x(x),c(c){ ... ` – enobayram Oct 06 '12 at 08:42
  • @enobayram i know that i'm pedantic on this but i think that your example is something that will work for the compiler but will make my code more difficult to read clearly, not a good trade off, if i need 4/5 arguments this will become a mess and at this point i would prefer a standard approach and go for a well defined constructor. I'm just thinking about this in terms of code readability and compactness that's why i'm interested in initialization lists. – axis Oct 06 '12 at 08:47
  • 2
    @axis there's virtually no other way of doing what you would like to do, and if you're that concerned about readability, you can always use whitespace in a clever way. for instance, use a separate line for each parameter and align them nicely. I'm sure any seasoned C++ developer can visually parse default values for function parameters after 2 bottles of vodka. If you're that concerned about how this will scale when you have 5 arguments, use `boost::parameter` library. – enobayram Oct 06 '12 at 08:52
1

There seems to be some confusion about what you want and how to achieve this. If I got you right this is what you want:

class A {
  public:  

    int x;
    std::string c;
    //default initization of A
    A():x(10), c("Hi") {}

    //initializing the values of A via parameters
    A(int x,std::string c):x(x),c(c){}

    ~A(){
      std::cout << "Deleting A()" << std::endl;
    }
};

class B : public A {
  public:

    B():A(20,"Hello"){
    }

    ~B(){
      std::cout << "Deleting B()" << std::endl;
    }
};

So in this example:

int main()
{
    A a;
    A a1(2, "foo");
    B b;
    return 0;
}
  • a.x == 10, a.c == "Hi"
  • a1.x == 2, a1.c == "foo"
  • b.x == 20, b.c == "Hello"
  • yes, but this is basically an initialization list + 1 constructor for A and an initialization list for B, the second constructor for A it's not a real list, it's more like a compact constructor because does not offer the real core of the init lists meaning that i it doesn't express a default value but just a method interface with assignment for variables. I think that at least for the parent classes is more appropriate the old constructor approach and i will use lists for child classes, if this are all the options, i think that initialization lists are not for my general case. – axis Oct 06 '12 at 09:36
  • First of all let me note, that you seem to have misunderstood initialization lists. This phrase only means the explicit constructor invocation of member variables/parent classes in a ctor, so all ctors in my example have initialization lists. Now to the meat: The inheritance method should abstract away parameters, so you do not pass all values. As it seems you want to pass all values I think you should rather go with composition, so you declare a member variable of type `A` inside `B` and pass an instance of `A` to copy construct the member. – Nobody moving away from SE Oct 06 '12 at 09:42
1

OK, I don't understand what exactly you want and why, but here's a suggestion, with C++11, you can do the following:

struct Base {
        int a;
        float b;
};

struct Derived: public Base {
        Derived(): Base{1,1.0} {}
};

int main() {
        Derived d;
}

as long as the base is a POD type.

I'd still prefer A(int x = 10,std::string c = std::string("Hi")):x(x),c(c){...} though.

IMHO, you need to review if you really need that much control over your base class in the first place. You're not really supposed to micro-manage a class from the outside like that, it's an indication of a flaw in your class hierarchy.

Community
  • 1
  • 1
enobayram
  • 4,650
  • 23
  • 36