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++类与对象第四篇:(初始化列表构造匿名对象隐式类型转换友元static成员内部类)