告诉子类对超类的受保护变量执行某些操作是否是一种好的做法(也许是一些已知的设计模式?)?

is it a good practice (maybe some known design pattern?) to tell subclasses to do something to the superclass's protected variables?

本文关键字:也许 设计模式 一种 子类 超类 执行 变量 操作 是否是 受保护      更新时间:2023-10-16

这是一个好模式吗?

BaseClass {
protected:
  Data m_data;
public:
  Interesting public_method() {
    //returns something interesting about m_data
    //what exactly depends on what the subclass put into m_data
  }
}
DerivedClass {
public:
  DerivedClass() { //properly populate m_data }
}

这样做的缺点是,事情太"松散",并且没有强制编译时间,因为子类程序员只是被告知要做一些事情,而不是真正被迫去做,所以我想知道经验丰富的c++程序员是否认为这是一种好的做法。

有什么好办法或更好的办法吗?

如果只有超类构造函数可以在子类构造函数完成工作后被强制调用。然后超类构造函数可以在中包含所需的参数

c++不允许这样做:(

如果只有超类构造函数可以在子类构造函数完成工作后被强制调用。然后超类构造函数中可以包含所需的参数

ctor初始值设定项列表非常强大。虽然只能使用表达式,但这些表达式可以包括函数调用。实际上,在构造函数主体中可以做的任何事情都可以从初始值设定项列表中实现。

当然,你必须尊重构造顺序,所以在初始化基类之前不能在基类上调用成员函数。但在初始化成员时可以访问基类。

class BaseClass
{
  Data m_data;
protected:
  BaseClass( Data&& data ) m_data(data) {}
public:
  Interesting public_method() {
    //returns something interesting about m_data
    //what exactly depends on what the subclass put into m_data
  }
};
class DerivedClass : public BaseClass
{
  static Data make_arg_into_data( const T& arg ) { /* calculate and return the right value for m_data */ }
  static Interesting make_arg_into_member_init( BaseClass* pThis, const T& arg ) { return pThis->public_method(); }
  V m_member;
public:
  DerivedClass( T arg ) BaseClass(make_arg_into_data(arg)), m_member(make_arg_into_member_init(this, arg)) {  }
};

这不是一个模式,而是一个反模式。经验法则:受保护的变量是坏的。继承的行为(通常从具体类继承)是不好的。当然,有时可能有理由忽视这两种情况,但如果你不说你真正想做什么,就很难说了。

在我看来,永远不应该使用受保护的变量。我更喜欢使用带有受保护方法的私有变量。通过这种方式,如果你想使用一个polymorfic对象,你可以覆盖你的方法,避免"坏的惊喜"。如果你的类是final,当然你可以直接使用受保护的var.

对我来说,这听起来不太正确。虽然派生类调用基类并非闻所未闻,但更常见的情况是,派生类会覆盖(虚拟)public_method来提供该信息。

或者,按照建议,使用工厂函数。或者是一个静态的"构造函数"成员函数,所以不调用new,而是调用一个调用newspecial function to make set the data的函数。

所有可能的解决方案。

听起来你可以使用一个工厂:

BaseClass {
private:
  Data m_data;
public:
  void setData(const Data& data) { m_data = data; }
  virtual Data generateData() = 0;
};
struct Factory
{
   template<class T>
   static BaseClass* getInstance()
   {
       BaseClass* t = new T;
       t->setData(t->generateData());
       return t;
   }
};

并称之为

BaseClass* p = Factory::getInstance<Derived>();

正如您所看到的,非抽象派生类被迫调用generateData,因此它们必须生成一个有效的Data

我只使用了原始指针以便于编写,不要:)