for*_*818 17 c++ inheritance object-slicing
有时,c ++默认允许切片可能会令人烦恼。例如
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
foo y = x; // <- I dont want this to compile!
}
Run Code Online (Sandbox Code Playgroud)
这将编译并按预期运行!但是,如果我不想启用切片怎么办?
什么是惯用的写法,foo以至于无法对任何派生类的实例进行切片?
Nat*_*ica 20
我不确定是否有命名的习惯用法,但是您可以向重载集中添加一个删除的函数,该函数比基类切片操作更匹配。如果您更改foo为
struct foo
{
int a;
foo() = default; // you have to add this because of the template constructor
template<typename T>
foo(const T&) = delete; // error trying to copy anything but a foo
template<typename T>
foo& operator=(const T&) = delete; // error assigning anything else but a foo
};
Run Code Online (Sandbox Code Playgroud)
那么您只能复制结构或复制分配foo给foo。任何其他类型都会选择功能模板,并且会出现有关使用已删除功能的错误信息。这确实意味着您的类以及使用它的类不再可以聚合。由于添加的成员是模板,因此不将它们视为复制构造函数或复制赋值运算符,因此您将获得默认的复制并移动构造函数和赋值运算符。
自2011年以来,惯用方式是使用auto:
#include <iostream>
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
auto y = x; // <- y is a bar
}
Run Code Online (Sandbox Code Playgroud)
如果您希望积极防止切片,可以采用多种方法:
通常,最可取的方法是使用封装,除非您特别需要继承(通常不需要),否则:
#include <iostream>
struct foo { int a; };
struct bar
{
bar(int a, int b)
: foo_(a)
, b(b)
{}
int b;
int get_a() const { return foo_.a; }
private:
foo foo_;
};
int main() {
bar x{1,2};
// foo y = x; // <- does not compile
}
Run Code Online (Sandbox Code Playgroud)
另一种更专业的方法可能是更改复制操作员周围的权限:
#include <iostream>
struct foo {
int a;
protected:
foo(foo const&) = default;
foo(foo&&) = default;
foo& operator=(foo const&) = default;
foo& operator=(foo&&) = default;
};
struct bar : foo
{
bar(int a, int b)
: foo{a}, b{b}
{}
int b;
};
int main() {
auto x = bar (1,2);
// foo y = x; // <- does not compile
}
Run Code Online (Sandbox Code Playgroud)