【问题标题】:Making initialization lists work with inheritance in C++使初始化列表与 C++ 中的继承一起使用
【发布时间】:2012-10-06 08:06:05
【问题描述】:

以下代码在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);
}

但它没有做应该做的事情,类型 B 无法更改它从 A 继承的 2 个变量的值。

关于为什么这不能正常工作的任何解释?

【问题讨论】:

  • 仅仅因为您将构造函数的参数命名为与您的成员相同,这并不意味着它们会被某种魔法复制,您必须手动执行此操作。

标签: c++ inheritance initializer-list


【解决方案1】:

您的基本构造函数采用这些值...并完全忽略它们!

改变这个:

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

到这里:

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

【讨论】:

  • 所以我需要定义一个构造函数并且我不能使用初始化列表?你能纠正我的例子吗?
  • 谢谢,问题解决了,我误解了初始化列表的工作原理。
  • @axis,我提供了修复。当您在派生构造函数的初始化列表中调用基本构造函数时,它会选择带有 (int, std::string) 参数的那个(唯一的一个),而后者又会忽略您给它的参数。 GCC 会发出类似警告:warning: unused parameter xwarning: unused parameter c
  • @axis 你为什么不用A(int x = 10,std::string c = std::string("Hi")):x(x),c(c){ ...
  • @axis 几乎没有其他方法可以做你想做的事,如果你那么关心可读性,你总是可以巧妙地使用空格。例如,为每个参数使用单独的行并很好地对齐它们。我确信任何经验丰富的 C++ 开发人员都可以在 2 瓶伏特加之后直观地解析函数参数的默认值。如果您担心当您有 5 个参数时这将如何扩展,请使用 boost::parameter 库。
【解决方案2】:

对于您想要什么以及如何实现这一点似乎有些困惑。 如果我猜对了,这就是你想要的:

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;
    }
};

所以在这个例子中:

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"

【讨论】:

  • 是的,但这基本上是 A 的初始化列表 + 1 个构造函数和 B 的初始化列表,A 的第二个构造函数不是真正的列表,它更像是一个紧凑的构造函数,因为不提供init 列表的真正核心意味着它不表示默认值,而只是一个带有变量赋值的方法接口。我认为至少对于父类来说,旧的构造方法更合适,我将为子类使用列表,如果这是所有选项,我认为初始化列表不适合我的一般情况。
  • 首先让我注意,您似乎误解了初始化列表。该短语仅表示 ctor 中成员变量/父类的显式构造函数调用,因此我的示例中的所有 ctor 都有初始化列表。现在开始:继承方法应该抽象出参数,所以你不要传递所有值。看起来你想传递所有值,我认为你应该使用组合,所以你在 B 中声明一个类型为 A 的成员变量,并传递一个 A 的实例来复制构造成员。
【解决方案3】:

好的,我不明白您到底想要什么以及为什么,但这里有一个建议,使用 C++11,您可以执行以下操作:

struct Base {
        int a;
        float b;
};

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

int main() {
        Derived d;
}

只要基数是POD 类型。

不过我还是更喜欢A(int x = 10,std::string c = std::string("Hi")):x(x),c(c){...}

恕我直言,您首先需要检查您是否真的需要对基类进行如此多的控制。你真的不应该像那样从外部对一个类进行微观管理,这表明你的类层次结构存在缺陷。

【讨论】:

  • 我不记得具体是什么,但({...}) 有问题。它应该只是{...}
  • 这给了:no matching function for call to ‘Base::Base(&lt;brace-enclosed initializer list&gt;)’
  • @chris 是的,我已经尝试使用 4.7.0 并且它可以工作,但不适用于 4.6.3
猜你喜欢
  • 2021-07-27
  • 1970-01-01
  • 2011-09-12
  • 1970-01-01
  • 2011-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多