C++继承问题

Posted

技术标签:

【中文标题】C++继承问题【英文标题】:C++ inheritance problem 【发布时间】:2009-11-24 11:20:20 【问题描述】:

这是我的课程:

父类,父对象

DerivedClass(继承自 ParentClass)、DerivedObj(继承自 ParentObj)。

ParentClass 有一个受保护的成员:

std::vector

DerivedClass 仅将 DerivedObj* 对象分配给该向量。

问题是:

当我使用 ParentClass 时,我想用类型的迭代器访问它的向量对象:

std::vector::const_iterator

当我使用 DerivedClass 时,我想用类型的迭代器访问它的向量对象:

std::vector::const_iterator

我怎样才能让它工作?

【问题讨论】:

您不应该在 STL 容器中使用指针元素,除非您 100% 确定没有人会修改它(例如使用 std::sort...)! STL 容器确实具有值语义,使用指针复制从来都不是一件好事! 我没有看到 std::sort 的问题...我宁愿谈论 ::erase() 方法或类似的东西:/ @fmuecke:您必须将指针视为指针,正确处理所有权问题,并区分“复制”和“克隆”。 (或浅拷贝与深拷贝)。 【参考方案1】:

您可以在 DerivedClass 类中使用虚拟覆盖,返回 ParentClass 返回类型的子类型...

struct ParentClass 
    virtual ParentObj* get( const size_t index ) 
       return m_objects[index];
    
 ;

 struct DerivedClass : public ParentClass 
    virtual DerivedObj* get( const size_t index ) 
       return dynamic_cast<DerivedObj*>( ParentClass::get(index) );
    
 ;

这两个函数都是相同的“vtable”条目,尽管它们的返回类型不同。

或者,您可以使用相同的技术来创建“继承的”迭代器。

除此之外,在DerivedClass 对象中包含DerivedObj* 的向量并不是一个坏主意,只要您保证它们也存在于ParentClass 的向量中。

【讨论】:

+1,提供 get() 方法看起来不错,并将强制转换限制为仅此函数。 如果类具有合适的不变量(例如,指针仅通过在 DerivedClass 中重写的虚拟成员函数添加到向量),您可以将其设为 static_cast。基本上是类型擦除。 是的,我知道。这听起来仍然不是一个好的代码实践(它出现在代码中的很多地方)。 @Jack,在重写的虚方法中返回原始返回类型的子类型称为协变返回类型。它本身并没有超载。【参考方案2】:

我有一个问题:为什么DerivedClass 继承自ParentClass

您需要多态行为,还是要重用ParentClass 的实现?

继承经常被严重滥用。真的。

然后是(典型的)容器类问题,以及如何公开其元素。不幸的是,与它们应该模拟的指针不同,语言对迭代器的支持很差(尽管它们有很多东西)。

所以,提醒一下:继承是一种is-a关系,对于代码重用存在Composition

在这里你可以完美地编写一个模板类来扮演容器的角色并提供通用的方法。

然后,对于阐述的问题...您可以编写自己的迭代器,具有正确的基派生关系,或者您可以选择预选一些算法(sortforeacherase_if ) 可以与用户提供的谓词一起使用。

template <class Value>
class Container

public:
  template <class Pred>
  void sort(Pred iPred);

  template <class Pred>
  Pred foreach(Pred iPred); // maybe a const-version ?

  template <class Pred>
  size_t erase_if(Pred iPred); // returns the number of erased
private:
  std::vector<Value> m_data;
;

然后,开始上课:

class ParentClass

public:
  virtual void foo() const;
private:
  Container<ParentObj*> m_data;
;

class DerivedClass: public ParentClass

public:
  virtual void foo() const;
private:
  Container<DerivedObj*> m_data;
;

尝试将多态性与代码重用分开,问题应该会变得更简单。

【讨论】:

【参考方案3】:

std::vector&lt; ParentObj* &gt;std::vector&lt; DerivedObj* &gt; 是两种完全不同的类型,因此您不能将一种隐式转换为另一种。您可以做的是使用dynamic_cast 将每个ParentObj* 转换为DerivedObj*。但通常这不是一个好的做法。

【讨论】:

我知道,这就是我现在所做的...没有别的办法吗?我想过让 vector 成为私有成员,而 DerivedClass 将拥有自己的私有成员:vector,但这也不是很优雅... 我认为这里更好的做法是在执行所需操作的对象上添加一个虚函数。因此在 ParentObj* 上调用它,runtie 将选择调用 ParentObj 或 DerivedObj 上定义的函数 有趣的想法。 > 虚拟无效衍生ObjDoesSomething() 断言(0); 我会考虑的,谢谢。【参考方案4】:

您不能使用基本的 C++ 继承来做到这一点。原因是当在DerivedClass 中使用时,编译器不知道向量只包含指向DerivedObj 的指针。我同意 Naveens 关于动态演员的声明。

您可以使用两个向量 std::vector&lt; ParentObj* &gt; mParentstd::vector&lt; DerivedObj* &gt; mDerived 以及一个虚拟的 Add() 函数。基本实现只会添加到mParentDerivedClass 中的 Add 函数也将添加到 mDerived

ParentClass 将具有 GetParentIterator() 函数,DerivedClass 将具有另一个(附加)函数 GetDerivedIterator()

【讨论】:

【参考方案5】:

你的继承设计错了。

DerivedClass 不能从 ParentClass 继承。 ParentClass 有一个受保护的成员 std::vector&lt; ParentObj* &gt;。 并且DerivedClass 必须有另一个受保护的成员std::vector&lt; DerivedObj* &gt;

你可以创建模板类

template<class T>
struct MyClass 
  protected:
    std::vector< T* > m_objects;
;

struct ParentClass : MyClass <ParentObj> ;
struct DerivedClass: MyClass <DerivedObj> ;

【讨论】:

它有什么变化?无论如何,为什么要使用受保护的成员,这超出了封装的目的... 这与 struct ParentClass std::vector m_objects; 有何不同?和 struct DerivedClass std::vector m_objects;; IE。两个不相关的类? 是的!不相关的类。它比继承更好。如果你想拥有通用的功能,你必须创建一个通用的基类。

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

C++继承之C++中不同的继承体系

C++菱形继承问题与虚拟继承原理

C++菱形继承问题与虚拟继承原理

C++中的动态继承

C++继承详解

C++继承详解