如何在C ++中初始化孙子中的祖父母(const)属性?

时间:2017-10-05 21:50:03

标签: c++ initialization

我知道这可能看起来像一个微不足道的问题,但我还没有找到一个优雅的C ++解决方案来解决以下问题。

我想表示一个对象“世界”的复杂(树状)层次结构。我们说Animals。每只动物都有一些基本的const属性。 例如name。然后它也有一些方法,但它们对于这个问题并不重要。

class Animal {
public:
    const char *GetName() const;

protected:
    const char *name;
};

class Insect : public Animal {
    ...
};

class Butterfly : public Insect {
    ...
};

在这个层次结构中,我想初始化每个派生(大)孩子中的name这是一个优雅的解决方案?

同样重要的是,在这个“世界”中,只有树叶的实例。也就是说,没有对象“Animal”或“Ins​​ect”。但是会有“蝴蝶”,“蜜蜂”或“蚊子”这些物品。

我知道执行此操作的“标准”方法是将name放入构造函数中:

Animal::Animal(const  char *name) : name(name) {}
Insect::Insect(const  char *name) : Animal(name) {}
Butterfly::Butterfly() : Insect("Butterfly") {}

但是如果有更多这些属性,派生类也需要一些初始化,层次结构有更多级别,它可能变得非常混乱:

Animal::Animal(const  char *name) : name(name) {}
Vertebrate::Vertebrate(const  char *name) : Animal(name) {}
Mammals::Mammals(const  char *name) : Vertebrate(name) {}
Ungulate::Ungulate(const  char *name) : Mammals(name) {}
Horse::Horse() : Ungulate("Horse") {}

我能看到的另一个选择是删除const并直接在孙子的构造函数中分配:

class Animal {
public:
    const char *GetName() const;

protected:
    std::string name;
};

Horse::Horse() {this->name = "Horse";}

但这也不是最优的,因为const丢失了,而且更容易出错(初始化可能会被遗忘)。

有没有更好的方法呢?

3 个答案:

答案 0 :(得分:2)

嗯 - 希望我没有因为那个答案而被锁定,但你可以使用实现name - 属性的虚拟基类。因此,您不必在基础类中通过层次结构传播初始化,而是可以直接解决"非常基础"具有name - 属性的构造函数。此外,你实际上会被强制在任何"孙子" -class中调用它,所以你不能偶然忘记它:

class NamedItem {
public:
    NamedItem(const char* _name) : name(_name) {}
    const char *GetName() const;

protected:
    const char *name;
};

class Animal : public virtual NamedItem {
public:
    Animal(int mySpecificOne) : NamedItem("") {}
};

class Insect : public Animal {
public:
    Insect(int mySpecificOne) : Animal(mySpecificOne), NamedItem("") {}

};

class Butterfly : public Insect {
};

答案 1 :(得分:2)

优雅解决方案是通过初始化传递参数。例如,如果“name”变量是Butterfly的名称(例如“sally”或“david”),那么很明显它必须通过初始化来完成。如果你发现它是丑陋的,就像它在这里一样,它可能表明你的数据分解/类层次结构是错误的。在您的示例中,每个Butterfly对象都具有一组相同的属性,这些属性实际上是指它们的类而不是每个实例,即它们是类变量而不是实例变量。这意味着“Butterfly”类应该有一个指向常见“Insect_Impl”对象的静态指针(可能有一个指向单个“Animal_Impl”对象的指针等)或一组被覆盖的虚函数。 (下面我只展示了一个级别的层次结构,但你应该能够计算出更多级别)

// Make virtual inherited functionality pure virtual
class Animal {
private:
   std::string objName; // Per object instance data
public:
   virtual ~Animal(std::string n): objName(n) {}
   virtual std::string const& getName() = 0; // Per sub-class data access
   virtual std::string const& getOrder() = 0; // Per sub-class data access
   std::string const& getObjName() { return this->objName; }
};
// Put common data into a non-inherited class
class Animal_Impl{
private:
   std::string name;
public:
   Animal_Impl(std::string n): name(n);
   std::string const& getName() const { return this->name; }
};
// Inherit for per-instance functionality, containment for per-class data.
class Butterfly: public Animal{
private:
  static std::unique< Animal_Impl > insect; // sub-class data
public:
  Butterfly(std::string n): Animal(n) {}
  virtual ~Butterfly() {}
  virtual std::string const& getName() override { 
      return this->insect->getName(); }
  virtual std::string const& getOrder() override {
      static std::string order( "Lepidoptera" );
      return order; }
};
// Class specific data is now initialised once in an implementation file.
std::unique< Animal_Impl > Butterfly::insect( new Animal_Impl("Butterfly") );

现在使用Butterfly类只需要每个实例数据。

Butterfly b( "sally" );
std::cout << b.getName() << " (Order " << b.getOrder() 
          << ") is called " << b.getObjName() << "\n";

答案 2 :(得分:0)

您的备选方案或任何替代方案name非const和受保护的问题是,无法保证子类将正确设置此属性。

以下课程给你什么?

class Animal {
public:
    Animal(const char* something)
    const char *GetName() const;

private:
    const char *name;
};

保证Animal接口的不变性,这在进行多线程处理时可能是一个很大的优势。如果一个对象是不可变的,那么多个线程就可以使用它而不是一个关键资源。

  

我知道&#34;标准&#34;这样做的方法是将name放入构造函数中:   ...但是如果有更多这些属性,派生类   还需要一些初始化,层次结构有更多的层次   变得一团糟

一点也不凌乱。假定对象A的成员只有一个地方被初始化,并且它位于其子类的构造函数中。