C++ 构造函数成员初始化器列表,对象切片

Posted

技术标签:

【中文标题】C++ 构造函数成员初始化器列表,对象切片【英文标题】:C++ Constructor member initializer lists, Object Slicing 【发布时间】:2018-03-20 23:36:38 【问题描述】:

我有两门课

class A 

public:
    virtual void doStuff() = 0;
;

class B : public A 
    int x;

public:
    virtual void doStuff() override  x = x*2; //just example function
;

还有另一个修改和使用前一个数据的类

class Foo 
    A a;

public:
    Foo::Foo(A &a_) : a(a_) 

;

现在我创建对象,并传递给 Foo 类

B b;
// edit b attributes, 

Foo foo(b);

所以在类构造函数的参数列表中我知道不存在对象切片的问题,因为是引用,但是在分配变量a(a_)的那一刻是什么情况?

由于我不知道对象b 将存在多久,我需要制作一个安全副本。我有很多来自 A 的不同派生类,甚至是派生自派生的。

会有对象切片吗?,

是否有解决方案,或者我需要传递指针(不想要这种方法)?

【问题讨论】:

【参考方案1】:

这会导致切片。内置多态性的 C++ 仅适用于指针/引用语义。

事实上:

class Foo 
  A a;

这甚至不会编译,因为A 不是一个具体的类。

要解决这个问题,首先创建virtual ~A();,然后将智能指针传递给A。可以是唯一的,也可以是共享的。


如果您无法使用自己的定制多态性。更简单的方法是将pImpl 智能指针填充为类的私有成员,并在持有类中实现复制/移动语义。 pImpl 可以有一个虚拟接口,包装类只是将行为的不可覆盖部分转发给它。

可以通过小缓冲区优化甚至限制大小的实例来扩展该技术,以避免堆分配。

所有这些都比直接使用内置的 C++ 对象模型更难,但它可以带来回报。

要查看这方面的一个著名示例,请检查 std::function<Sig>,它是一种行为多态的值类型。

【讨论】:

【参考方案2】:

将使用您当前拥有的内容进行对象切片。您在Foo 的构造函数中调用A 复制构造函数,并且没有虚拟构造函数。 拥有A 类型的成员变量只会在Foo 的实例中为A 的实例保留足够的空间。只有指针和引用(实际上是指针)的动态绑定,而不是成员变量。

你必须使用指针来解决这个问题,或者你可以重新考虑你是否真的需要这样的设置。

【讨论】:

【参考方案3】:

是的,有切片。

必须进行切片,因为 B 不适合在 A 中,但它是 A 存储在类 Foo 中。 B 部分被“切掉”以适应;因此得名。

【讨论】:

以上是关于C++ 构造函数成员初始化器列表,对象切片的主要内容,如果未能解决你的问题,请参考以下文章

C++成员初始化列表(构造函数后加冒号:)(用于在构造函数中初始化类成员变量,可以避免使用构造函数体内的赋值语句,可以确保成员变量在对象构造之初就已经被正确初始化,提高代码的性能和可读性)

C++构造函数的初始化列表

C++类与对象第四篇:(初始化列表构造匿名对象隐式类型转换友元static成员内部类)

[ C++ ] 类与对象(下) 初始化列表,友元,static成员,内部类

C++类和对象下

如何使用大小和值构造函数使用成员初始化器列表初始化用户定义对象的向量