【问题标题】:C++ initializer list members still calling default constructor?C++ 初始值设定项列表成员仍在调用默认构造函数?
【发布时间】:2020-11-16 09:50:26
【问题描述】:

我在初始化我正在处理的类的 shared_ptr 成员时遇到问题。

我有两个班级,AB

class A {
  int _x;
  int _y;

  public:
    A(int, int);
};

A::A(int x, int y) {
  _x = x;
  _y = y;
}

class B : public A {
  std::shared_ptr<A> _a;

  public:
    B(std::shared_ptr<A>);
};

B::B(std::shared_ptr<A> a) : _a(a) {}

int main() {
  std::shared_ptr<A> a = std::make_shared<A>(1, 2);

  B b(a);

  return 0;
}

我只想让B 类将std::shared_ptr 保存到A 类的实例中。 但是,我在B 的构造函数中收到错误no matching function for call to A::A()

我很困惑,因为我认为初始化列表的目的是避免隐式调用成员变量的默认构造函数,但它似乎仍在尝试调用 A 的默认构造函数。

感谢任何解释。

编辑:经过更多的混乱,如果B 没有从A 继承,它似乎可以正确遵守。仍然不确定为什么从A 继承会导致A 的默认构造函数被B 的构造函数调用。

【问题讨论】:

  • A的默认构造函数在B的构造函数中被隐式调用:B::B(std::shared_ptr&lt;A&gt; a) : _a(a) {}。因为B 继承自A,所以在构造B 时必须调用A 的构造函数。
  • 为什么要BA继承并且同时拥有shared_ptrA
  • 当你从一个类继承时,新类的每个对象都包含一个超类的对象。 class B : A 表示每个B包含一个A。您需要以某种方式初始化该对象。如果你不在成员初始化列表中初始化它(你不是),那么它默认使用不存在的默认构造函数。因此B 必须被拒绝。
  • 如果你只是想让B 持有shared_ptrA,为什么B 派生自A,这使得它不仅仅持有shared_ptr
  • @LouisGo 这就是 OP 已经发现的:it seems like it complies properly if B does not inherit from A

标签: c++ initialization shared-ptr initializer-list


【解决方案1】:

由于B 派生自A,因此每个B 都是A。这意味着要构造一个B,您还必须构造一个A。但是,你不告诉B的构造函数如何构造A,所以使用默认构造函数。

如果您希望 B 派生自 A 并且仅持有 shared_ptrA,则 A 不得做任何您不希望 B 做的事情。这里,A_x_y

不清楚你真正想要什么,但也许你想要一些其他基类,AB 都派生自它,只有 AB 应该有。

C++ 中继承的经典示例类似于 Instrument 是具有 Play 等成员的基类,以及 TrumpetClarinet 等派生类具有使某些特定乐器成为小号的东西。只有Trumpets 和Clarinets 的共同点才应该在Instrument 中。

【讨论】:

  • 感谢您的回答!更详细地说明 A 和 B 的目的:我目前正在学习装饰器设计模式。在这个例子中,B 是一个基本的装饰类,而A 是一个被装饰的具体类。我最终将A 拆分为两个类,一个抽象基类和一个具体实现(“普通”或未装饰)类,并且装饰器仅从抽象基类继承。我修复了我的编译器错误,现在我只专注于不引入任何反模式:) 再次感谢
【解决方案2】:

当您调用B b(a); 时,您使用构造函数B::B(std::shared_ptr&lt;A&gt; a) 构造了一个B 对象。

问题在于你对这个构造函数的定义。 B 继承自 A,这意味着 B 不仅仅是一个 B,而是实际上是一个 AB 堆叠在一起。 A 是基类,所以它在底部。因此,当您尝试构造B 对象时,代码必须首先在底部/底部构造A 对象,以形成B 对象存在的基础。这意味着您实际上必须告诉代码如何构造A 对象。

因为您为 A 定义了一个带有两个 int 变量的构造函数,所以编译器不会为 A 生成默认构造函数。因此,您必须明确写出应该如何在B 中构造A。构造A 对象可以在B 对象的构造函数的初始化列表中完成:

B::B(std::shared_ptr<A> a) : A(0,0), _a(a) {}

【讨论】:

  • “因为你为 A 定义了一个带有两个 int 变量的析构函数”,构造函数的析构函数错字了吗?
  • @LouisGo 是的,谢谢!我已经纠正了错字:)
猜你喜欢
  • 1970-01-01
  • 2018-11-24
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 2011-08-09
  • 1970-01-01
  • 2018-11-29
  • 2015-01-12
相关资源
最近更新 更多