【问题标题】:C++ Inheritance with pure virtual functions纯虚函数的 C++ 继承
【发布时间】:2011-05-14 20:09:31
【问题描述】:

我正在尝试创建一个用作基础对象的类,然后将其子类化(=实现)以服务于各种目的。

我想定义一个或多个纯虚函数,以便基类的子类是必需的,并且不会忘记实现它们。

有一个警告,纯虚函数的签名包括基础对象的类型。一旦子类化,函数定义当然不再匹配基类定义。例如:

class BaseItem 
{
public:
    virtual std::string getDifferences(const BaseItem& item) = 0;
}

所以,在派生类中我想做:

class DerivedClass : public BaseItem
{
public:
    virtual std::string getDifferences(const DerivedClass& item) = 0;
private:
    std::string derivedItemCustomObject;
}

编译器当然不会接受。我当然可以将其设为BaseItem,但是我不能在派生类中使用任何对象。

我必须使用强制转换来完成此操作吗?

如果我的意图/问题不清楚,请告诉我。

【问题讨论】:

  • 是的,执行此操作的常用方法是将参数设为 BaseItem,并可能根据派生类的要求使用 dynamic_cast 进行转换。
  • @ybungalobill @BoPersson Look here.
  • @muntoo: look here
  • 如果一个方法不应该被基类的接口调用,那么它就不应该存在于基类的接口中。如果 Base::getDifferences 的唯一原因是“不要忘记实现”,那么 Base::getDifferences 不应该存在。
  • @muntoo:您从 150 多个单词的问题中删除了一个(!)单词,并通过人们同意可以从大约 40 个问题中删除大约 30 个不必要单词的示例来支持它字...??更不用说这个词不是问候,而是一个“谢谢”。

标签: c++ inheritance


【解决方案1】:

无需更改函数签名。看下面:

class BaseItem 
{public:
    virtual std::string getDifferences(const BaseItem& item) = 0;
};

class DerivedClass : public BaseItem
{public:
    virtual std::string getDifferences(const BaseItem& item)  // keep it as it's
    {
       const DerivedClass& derivedItem = static_cast<const DerivedClass&>(item);
    }
};

可以毫无顾忌地使用static_cast&lt;&gt;,因为DerivedClass::getDifferences() 只为DerivedClass 对象调用。为了说明,

BaseItem *p = new DerivedClass;
DerivedClass obj;
p->getDifferences(obj);  // this always invoke DerivedClass::getDifferences

如果您担心有时可能会将任何其他派生类对象作为参数传递给该方法,请改用dynamic_cast&lt;&gt;,如果该转换失败则抛出异常。

【讨论】:

  • 这似乎是完成我所寻找的最简单的方法,谢谢!
  • 谢谢@iammilind,你成就了我的一天 :)
  • @Eric,很高兴听到......它有帮助。但是,请确保您将正确的类型对象传递给函数。
【解决方案2】:

目前尚不清楚您要达到的目标。假设编译器允许你这样做(或者你通过强制转换来做到这一点),那么它将在类型系统中打开以下漏洞:

class BaseItem 
{
public:
    virtual std::string getDifferences(const BaseItem& item) = 0;
};

class DerivedClass : public BaseItem
{
public:
    virtual std::string getDifferences(const DerivedClass& item) 
    {
        item.f(); 
        // ... 
    }

    void f() const {}
};

class DerivedClass2 : public BaseItem
{
public:
    virtual std::string getDifferences(const DerivedClass2& item) { ... }
};

void g()
{
    BaseItem* x = new DerivedClass;

    // oops, calls DerivedClass::f on an instance of DerivedClass2
    x->getDifferences(DerivedClass2());
}

你的设计可能是错误的。

【讨论】:

  • 这不是一个真正的设计,我希望这里的答案能帮助我想出一个好的设计。
【解决方案3】:

我假设编译器接受但 DerivedClass::getDifferences 不会覆盖 BaseItem::getDifferences。这是实现您显然想要的一种方法

template <typename T>
class DerivedHelper: public BaseItem {
public:
   virtual std::string getDifferences(const BaseItem& item) {
      getDifferences(dynamic_cast<const T&>(item));
   }
   virtual std::string getDifferences(const T& item) = 0;
}; 

class DerivedClass : public DerivedHelper<DerivedClass>
{
public:
   // not more needed but providing it will hide getDifferences(const BaseItem& item)
   // helping to statically catch some cases where a bad argument type is used.
   virtual std::string getDifferences(const DerivedClass& item) = 0;
private:
   std::string derivedItemCustomObject;
};

但请注意,如果参数不属于正确的类,则运行时检查将引发异常。

【讨论】:

  • 顺便说一句,这可能是我第一次发现具有不同签名的函数成员隐藏成员函数的用途。
  • 最初喜欢它后我不确定隐藏。可能有用,但我很谨慎。 struct SillyExample { BaseItem &x;模板 void operator()(T &n) { n.getDifferences(x); } }; // 你想防止这种情况发生吗?
  • @Fred,我有时不确定我是否想要静态类型 :-) 在表现力和安全性之间总是相同的权衡。
【解决方案4】:

实现此目的的一种方法是使用模板并将参数设为派生类型的类型

template <typename T>
class BaseItem {
public:
  virtual std::string getDifferences(const T& item) = 0;
};

class DerivedClass : public BaseItem<DerivedClass> {
public:
  virtual std::string getDifferences(const DerivedClass& item) {
    // Implement it here
  }
};

【讨论】:

    【解决方案5】:

    您应该使用从 BaseItem 到 DerivedClass 的转换 + 运行时检查给定的 BaseItem 是否是 DerivedClass 实例。

    【讨论】:

      猜你喜欢
      • 2015-08-07
      • 1970-01-01
      • 2018-10-09
      • 1970-01-01
      • 1970-01-01
      • 2012-08-06
      • 1970-01-01
      • 1970-01-01
      • 2012-01-31
      相关资源
      最近更新 更多