纯虚函数的 C++ 继承

Posted

技术标签:

【中文标题】纯虚函数的 C++ 继承【英文标题】:C++ Inheritance with pure virtual functions 【发布时间】: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 个不必要单词的示例来支持它字...??更不用说这个词不是问候,而是一个“谢谢”。 【参考方案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 实例。

【讨论】:

以上是关于纯虚函数的 C++ 继承的主要内容,如果未能解决你的问题,请参考以下文章

C++笔记--面向对象(OOP)编程基础--虚函数纯虚函数多继承

C++中的继承和纯虚函数

C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解

C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解

C++纯虚函数

C++的纯虚函数实现和头文件