首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >如何创建只调用大父构造函数的构造函数?

如何创建只调用大父构造函数的构造函数?
EN

Stack Overflow用户
提问于 2021-04-22 11:15:04
回答 2查看 149关注 0票数 2

我在一个层次结构中有3个类(称为A、B和C),其中B扩展A和C扩展B。C的定义要求调用A的构造函数,所以我试图在B中创建一个构造函数,但是编译器告诉我C的构造函数必须同时初始化A和B,这在我看来是违反直觉的,因为它应该只初始化一次。

下面是更好地说明我所面临的问题的代码:

代码语言:javascript
运行
AI代码解释
复制
#include <iostream>

struct A {
  A(std::string name) : name_(name) {
    std::cout << "A ctor called: " << name << std::endl;
  }
  std::string name_;
};

struct B : virtual public A {
  // This constructor is required or else subclasses cannot be constructed properly
  B(std::string name) : A(name) {
    std::cout << "B ctor called: " << name << std::endl;
  }
};

struct C : virtual public B {

  // ERROR: constructor for 'C' must explicitly initialize the base class 'A' which does not have a default constructor
  // C() : B("hey") {} 

  // ERROR: constructor for 'C' must explicitly initialize the base class 'B' which does not have a default constructor
  // C() : A("hey") {} 

  // ok... but have to pass the same name twice & init'ed twice!
  C() : A("wat"), B("hey") {
    std::cout << "C ctor called" << std::endl;
  }

  // gcc reorders the constructor invocations...
  // here it's written as B then A but it would be init'ed in the order of A then B
  // C() : B("hey"), A("wat") {
  //   std::cout << "C ctor called" << std::endl;
  // }

  // ok... we can just pass a name but it's still init'ed twice!
  // C(std::string name) : B(name), A(name) {}
};

int main() {
  C c;
  std::cout << c.name_ << std::endl;
}

当我运行密码时,我得到:

代码语言:javascript
运行
AI代码解释
复制
A ctor called: wat
B ctor called: hey
C ctor called
wat

我的问题是:

  1. 是否有一种更简洁的方式来编写它,使我不必显式地调用A&B的构造函数?

  1. 为什么输出显示在稍后时间设置hey,但是name_字段包含wat (前面设置的)?
EN

回答 2

Stack Overflow用户

发布于 2021-04-22 11:55:06

在你的解释中,你并没有说你实际上在做这件事。在看了你的代码之后,我走了。首先,扪心自问:“为什么我需要虚拟的派生?”很可能没有很好的理由去做。如果你认为有一个很好的理由,很可能是没有。如果您仍然坚持实际上派生,没有很好的理由,请参见:https://isocpp.org/wiki/faq/multiple-inheritance

票数 1
EN

Stack Overflow用户

发布于 2021-04-22 14:30:10

一个几乎继承的类总是由“大多数派生类”继承的。您的类C (大部分)完全等同于:

代码语言:javascript
运行
AI代码解释
复制
struct C : virtual public A, virtual public B {

对于所有意图和目的,C继承自A (当它是派生最多的类时),无论您是否显式地声明它。这就是虚拟继承在C++中的含义。

每个类的构造函数负责构造它继承的所有类。这包括几乎继承的类。它们是否是显式继承的。即使你有:

代码语言:javascript
运行
AI代码解释
复制
struct C : virtual public B {

由于类C仍然是从A继承的(不管您喜欢与否),它的所有构造函数都必须正式构造A,除非A有一个合适的默认构造函数,在这种情况下,当E 215E 215E 119是最派生的类E 220时,A得到默认构造函数(稍后将详细介绍)。

事情变得更复杂了。假设你尽了自己的职责:

代码语言:javascript
运行
AI代码解释
复制
C::C(...) : A{ ... }, B{ ... } // The actual parameters are irrelevant

假设您现在声明了以下内容之一:

代码语言:javascript
运行
AI代码解释
复制
C an_instance_of_c{ ... };

您成功地为C (“大多数派生类”)调用了这个构造函数,并且按照您的说明,它顺从地构造了A和B。

现在假设您创建了从D继承的C

代码语言:javascript
运行
AI代码解释
复制
struct D : public C { ... }

然后继续构建一个D

代码语言:javascript
运行
AI代码解释
复制
D::D(...) : A{ ... }, B{ ... }, C{ ... }

您刚刚发现,D现在负责构建A和B,原因与我刚才解释的相同。当然,它还负责构建C。

现在,假设C的构造函数的参数最终调用了上面所示的构造函数。

嗯,你猜怎么着,即使你写了构造函数来构造A和B,这个构造函数实际上将是它所做的一切,,除了。D现在负责构建A和B,但是即使调用相同的C构造函数,它也会执行而不是做任何与A和B相关的操作。毕竟,A和B已经是由D构建的了。但是C将构建它所构建的其他所有东西。

当您有一个虚拟继承类时,您显式或隐式定义的每个构造函数在C++编译器中自动编译的代码--两个单独的构造函数:一个构造所有虚拟继承的类,另一个不构造。您的C++编译器还将生成代码来调用每个构造函数的适当版本,而这个类要么是正在构造的“大多数派生类”,要么不是。

由于虚拟继承所造成的额外复杂性,以及由此而产生的所有陷阱和问题(例如,即使您私下和实际上继承了一个类,子类也可以公开地从同一个类继承,并且保护和公共访问您认为您私下继承的所有东西),您偶尔会遇到这种情况。当然,所有这些都是正确的,但是如果您完全了解虚拟继承是如何工作的,以及它是如何工作的,那么使用虚拟继承没有什么错,而且它允许在C++中完成任何一个对等程序都无法完成的任务。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67219462

复制
相关文章
【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )
1 . 构造函数个数 : Kotlin 类定义时需要指定主构造函数 , 还可以指定 0 ~ 多个次构造函数 ;
韩曙亮
2023/03/27
4.3K0
【C++】构造函数调用规则 ( 默认构造函数 | 默认无参构造函数 | 默认拷贝构造函数 | 构造函数调用规则说明 )
如果 C++ 类中 没有定义构造函数 , C++ 编译器会自动为该类提供一个 " 默认的无参构造函数 " , 函数体为空 , 不做任何操作 ;
韩曙亮
2023/10/15
1.4K0
【C++】构造函数调用规则 ( 默认构造函数 | 默认无参构造函数 | 默认拷贝构造函数 | 构造函数调用规则说明 )
dotnet C# 只创建对象不调用构造函数方法
有时我期望只是创建出对象,但是不要调用对象的构造方法,可以通过使用 FormatterServices 的 GetUninitializedObject 函数来实现只创建对象不调用构造函数方法
林德熙
2021/12/24
8600
java构造函数调用另一个构造函数_java中的构造函数
* 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法
用户7886150
2021/04/29
4.7K0
JAVA & .NET创建对象构造函数调用顺序
基类静态初始化块——当前类静态初始化块——基类初始化块——基类构造函数——当前类初始化块——当前类构造函数
雪飞鸿
2019/03/08
1.1K0
虚函数中构造函数的调用顺序
1 /*曾经有段时间一直被构造函数中的虚函数所困扰,现在通过自己重新学习了一遍,标注一下容易忘记的知识*/ 2 #include<iostream> 3 using namespace std; 4 class Base0 5 { 6 public: 7 Base0(int var):var0(var) 8 { 9 cout<<"Construct base0"<<endl; 10 } ; 11 int var0; 12 void fun()
Gxjun
2018/03/22
3.7K0
虚函数中构造函数的调用顺序
【C++】构造函数意义 ( 构造函数显式调用与隐式调用 | 构造函数替代方案 - 初始化函数 | 初始化函数缺陷 | 默认构造函数 )
C++ 提供的 构造函数 和 析构函数 作为 类实例对象的 初始化 和 销毁 方案 ;
韩曙亮
2023/10/15
9360
【C++】构造函数意义 ( 构造函数显式调用与隐式调用 | 构造函数替代方案 - 初始化函数 | 初始化函数缺陷 | 默认构造函数 )
【C++】构造函数分类 ① ( 构造函数分类简介 | 无参构造函数 | 有参构造函数 | 拷贝构造函数 | 代码示例 - 三种类型构造函数定义与调用 )
无参构造函数 : 只负责为指针类型的成员变量分配内存 , 并赋值一个初始值 , 没有具体的赋值信息 , 该初始化只能为 成员变量 赋值一个默认值 ;
韩曙亮
2023/10/15
5270
【C++】构造函数分类 ① ( 构造函数分类简介 | 无参构造函数 | 有参构造函数 | 拷贝构造函数 | 代码示例 - 三种类型构造函数定义与调用 )
java构造函数方法声明无效_如何构造函数
Java构造函数,也叫构造方法,是JAVA中一种特殊的函数。与函数名相同,无返回值。
全栈程序员站长
2022/10/05
1.7K0
C++构造函数 | 构造函数
C++在建立一个对象时,常常需要作某些初始化,如果一个数据成员未被赋值,则它的值是不可预知的,因为在系统为它分配内存时,保留了这些存储单元的原状,这就成为了这些数据成员的初始值,在C++中,对象是一个实体,它反映了客观事物的属性,是应该有确定的值的。
小林C语言
2021/01/26
2.3K0
C++构造函数 | 构造函数
js 中的构造函数,构造函数作用,构造函数和普通函数的区别
函数的定义方式: 1.声明式函数定义: function 函数名 (){};这种定义方式,会将函数声明提升到该函数所在作用域的最开头,也是就无论你在这个函数的最小作用域的那儿使用这种方式声明的函数,在这个作用域内,你都可以调用这个函数为你所用。 2.函数表达式:let fun = function(){}; 此方式定义的函数,只能在该作用域中,这段赋值代码执行之后才能通过fun()调用函数,否则,由于变量声明提升,fun === undefined。 3.new Function 形式: var fun1 = new Function (arg1 , arg2 ,arg3 ,…, argN , body );Function构造函数所有的参数都是字符串类型。除了最后一个参数, 其余的参数都作为生成函数的参数即形参。这里可以没有参数。最后一个参数, 表示的是要创建函数的函数体。
全栈程序员站长
2022/10/04
3.6K0
通过工厂函数、构造函数创建对象
当我们有多个变量的结构非常类似时,如下所示,反复书写结构过于麻烦,我们可以定义一个工厂函数来创建对象
很酷的站长
2022/12/21
8210
通过工厂函数、构造函数创建对象
禁止在构造函数里调用虚函数
在构造函数中调用虚函数会导致程序出现莫名其妙的行为,这主要是对象还没有完全构造完成。下面我们先来看一段代码:
喵叔
2020/09/08
1.7K0
【说站】javascript new如何调用构造函数
以上就是javascript new调用构造函数,希望对大家有所帮助。更多Javascript学习指路:Javascript
很酷的站长
2022/11/23
1.3K0
【说站】javascript new如何调用构造函数
Java构造函数调用顺序问题
今天对Java的构造函数调用顺序进行研究,使用的是与C++类似的方法,即不对源码进行研究,而是直接通过打印代码对构造函数的调用顺序进行研究。
用户7886150
2021/04/20
1.1K0
Java构造函数调用顺序问题
今天对Java的构造函数调用顺序进行研究,使用的是与C++类似的方法,即不对源码进行研究,而是直接通过打印代码对构造函数的调用顺序进行研究。
全栈程序员站长
2022/06/28
7490
Groovy 元组构造函数创建
Groovy 1.8添加了@TupleConstructor注释。 通过这个注释,我们可以在编译时自动创建一个元组构造函数。 因此构造函数可以在编译的类中找到。 对于类中的每个属性,将使用默认值创建构造函数中的参数。 类中定义的属性的顺序还定义了构造函数中参数的顺序。 因为参数具有默认值,所以我们可以使用Groovy语法,并在使用构造函数时将参数留在参数列表的末尾。
白石
2019/09/18
1.3K0
【C++】构造函数与析构函数概念简介 ( 构造函数和析构函数引入 | 构造函数定义与调用 | 析构函数定义与调用 | 代码示例 )
在 C++ 语言中 , 创建对象时 , 需要进行对象创建的初始化工作 , 如 : 创建集合数组 , 为成员变量设置初始值 ;
韩曙亮
2023/10/15
3980
【C++】构造函数与析构函数概念简介 ( 构造函数和析构函数引入 | 构造函数定义与调用 | 析构函数定义与调用 | 代码示例 )
小朋友学C++(10):子类构造函数调用父类构造函数
从哲学层面来看,子类会继承父类除private以外的所有成员。 因为构造函数是公有的,所以理所当然地会被子类继承。 程序1: #include <iostream> using namespace std; class Shape { public: Shape() { cout << "Shape's constructor method is invoked!\n"; } }; class Rectangle : public Shape { public:
海天一树
2018/04/17
1.5K0
【C++】构造函数初始化列表 ⑤ ( 匿名对象 生命周期 | 构造函数 中 不能调用 构造函数 )
下面的代码中 , 在 fun 函数中 , 调用 Student(18, 180) 代码 , 创建 Student 类型的匿名对象 , 匿名对象的生命周期 只存在于 这一行代码 , 该行代码执行完毕后 , 匿名对象 就会被 析构 ;
韩曙亮
2023/10/15
2770
【C++】构造函数初始化列表 ⑤ ( 匿名对象 生命周期 | 构造函数 中 不能调用 构造函数 )

相似问题

C++大父级默认构造函数调用

13

调用多个构造函数--只调用同一个构造函数?

40

从构造函数调用构造函数

24

构造函数调用其他构造函数。

10

从动态创建的子构造函数调用父构造函数

13
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档