c++ - 对象切片会破坏封装吗?

标签 c++ oop inheritance encapsulation

在我看来,在 OOP 中,公共(public)接口(interface)的一个作用是确保对象始终处于有效状态(这是为什么你不能 memcpy 到非-pod 类型)。例如。一个非常基本的、迂腐的例子:(请忽略类 java 的 set/get):

class Restricted {
protected:
  int a;

public:
  Restricted() : a{0} {}

  // always returns a non-negative number
  auto get() const -> int {
    return a;
  }

  auto set(int pa) -> void {
    if (pa < 0) 
      a = 0;
    else
      a = pa;
  }

  auto do_something() {
    // here I code with the assumption that
    // a is not negative
  }
};

在此示例中,类 Restricted 的建模方式是 Restricted 对象始终包含一个非负数。这就是我为 Restricted 定义有效状态的方式。通过查看接口(interface),我可以说 Restricted::get 将始终返回一个非负数。用户无法让 Restricted 持有负数。

a 受到保护,让选项可以轻松扩展类。因此,让我们使用允许所有数字的类型扩展 Restricted:

class Extended : public Restricted {
public:
  Extended()  { a = -1; }

  auto set(int pa) -> void {
    a = pa;
  }

  auto do_something() {
    // now a can be negative, so I take that into account
  }
};

乍一看一切正常。 Extended 不会修改 Restricted 或其行为。 Restricted 仍然是一样的,我们只是添加了另一种也允许负数的类型。

除了我们最初对 Restricted 的假设不再成立。用户可以轻松获得包含负数的 Restricted 对象(处于无效状态的对象),因为:

C++ 允许在没有任何警告的情况下进行对象切片:

Restricted r = Extended{};

// or

Extended e;
e.set(-24);

Restricted r = e;

// and then:

r.do_something(); // oups

有些东西没有加起来:

  • Extended 创建为 Restricted 的子类是错误的吗?如果是,为什么?
  • 如果我希望 a 始终为非负值,那么将 a 设置为 protected 是否错误?为什么? protected 不允许在我的类(class)中改变行为。
  • C++ 允许对象切片是错误的吗?
  • 以上所有
  • 别的

最佳答案

was it wrong to set a as protected if I expect a to be always non-negative? Why?

是的,这是错误的。 Restricted 的接口(interface)要求 a 是非负数。那就是类型设置的不变量。而且它没有虚拟函数来允许派生类覆盖该不变量。

通过违反该不变性(并且由于您好奇地缺少 virtual 函数),Extended 打破了 OOP 的基本规则:派生类实例应该能够被视为(公共(public))基类的实例。那并不意味着切片;我的意思是,您应该能够将 Extended 传递给一个函数,该函数接受对 Restricted 的指针/引用,并且一切都应该像在与 扩展

was it wrong to create Extended as a subclass of Restricted? If so, why?

错误的是:

  1. 使 a protected 而不是 private

  2. Restricted 的接口(interface)设为非虚拟

is it wrong for C++ to allow object slicing?

没有。

关于c++ - 对象切片会破坏封装吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39453589/

相关文章:

c++ - R6010 abort() 已被调用

c++ - C++中的指针数组

javascript - 如何从 Javascript 中的参数访问对象内的对象?

swift - 在通用元类型函数中返回具有特定元类型的对象

php - 通过多用户网页重复执行linux程序而不崩溃

c++ - CMakeList : Dependencies for static libraries

ruby - 如何在 Ruby 中的命名空间类上调用命名空间方法

matlab - 您如何在 MATLAB 中从类外部将类属性设置为只读?

c++ - 如何让派生类使用基实现来满足接口(interface)

java - Java中的继承