我的看法是,在OOP中,公共接口的作用是确保对象始终处于有效状态(这是不能将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定义有效状态的方式。通过查看接口,我可以说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始终为非负数,将protected设置为a是错误的吗?为什么? protected不允许更改我班级的行为。
C++允许对象切片是错误的吗?
以上全部
其他的东西

最佳答案

如果我期望a始终为非负数,则将a设置为保护是错误的吗?为什么?


是的,这是错误的。 Restricted的界面要求a为非负数。这就是类型设置的不变性。而且它没有virtual函数,以允许派生类覆盖该不变式。

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


  将Extended创建为Restricted的子类是错误的吗?如果是这样,为什么?


这样做是错误的:


Extended a代替protected
private的界面设置为非Restricted



  C ++允许对象切片是错误的吗?


没有。

09-29 21:01