在C ++中覆盖成员变量

时间:2008-11-18 12:18:11

标签: c++ oop

我在某些C ++代码中遇到了一个棘手的问题,最容易使用代码来描述。我的课程类似于:

class MyVarBase
{
}

class MyVar : public MyVarBase
{
    int Foo();
}

class MyBase
{
public: 
    MyBase(MyVarBase* v) : m_var(v) {}
    virtual MyVarBase* GetVar() { return m_var; }
private:
    MyVarBase* m_var;
}

我还有一个MyBase的子类,需要有一个MyVar类型的成员,因为它需要调用Foo。将Foo函数移动到MyVarBase不是一种选择。这样做是否有意义:

class MyClass : public MyBase
{
public:
    MyClass(MyVar* v) : MyBase(v), m_var(v) {}
    MyVar* GetVar() { return m_var; }
private:
    MyVar* m_var;
}

这似乎工作但看起来非常糟糕,我不确定它是否会导致内存泄漏或破坏复制构造函数。我的其他选项可能是在MyClass中将MyVar变量命名为其他东西,但是它等于基础中的m_var指针,或者在MyVar类型上对MyBase进行临时处理。

所有这些选项似乎并不理想,所以我想知道是否有其他人遇到过这样的情况,以及是否有一个好的方法让它发挥作用。

3 个答案:

答案 0 :(得分:13)

执行此操作的正确方法是仅在基类中使用变量。由于派生类知道它必须是动态类型MyVar,这是完全合理的:

class MyClass : public MyBase
{
public:
    MyClass(MyVar* v) : MyBase(v) {}
    MyVar* GetVar() { return static_cast<MyVar*>(MyBase::GetVar()); }
}

由于MyVar是从MyVarBase派生的,如果GetVar是虚拟的,GetVar的不同返回类型仍然有效(如此处所示)。请注意,使用该方法,MyBase中必须没有可以将指针重置为不同的函数。

请注意,static_cast在这种情况下是正确的演员。正如一位评论者所建议的那样,使用dynamic_cast将告诉GetVar的读者和用户MyBase::GetVar()可以返回指向非MyVar类型的对象的指针。但这并不能反映我们的意图,因为您只能通过MyVar。结果是软件开发中最重要的事情。你可以做的是断言它是非空的。它将在运行时中止,并在项目的调试版本中显示错误消息:

MyVar* GetVar() { 
    assert(dynamic_cast<MyVar*>(MyBase::GetVar()) != 0);
    return static_cast<MyVar*>(MyBase::GetVar()); 
}

答案 1 :(得分:2)

在不了解更多背景的情况下,很难肯定地说,但我会重新考虑你是否首先需要这个类层次结构。你真的需要MyVarBase和MyBase类吗?你能逃脱构图而不是继承吗?如果你模拟使用上述类的函数和类,你可能根本不需要类heirarchy。也许CRTP也可以提供帮助。在C ++中有很多替代方法可以使用虚函数(在某种程度上,一般来说都是继承)。

无论如何,你自己建议的解决方案当然不应该泄漏任何内存(因为它没有分配任何内存),也不会破坏默认的拷贝构造函数(因为它只是执行成员方式的复制。现在你只是在派生类中有一个额外的成员,但也会被复制)。 当然,你得到了保持两个变量同步的额外问题,所以我仍然不喜欢那个解决方案。

或者,您可以从基类中删除变量。使GetVar()函数成为纯虚拟的,因此它只在派生类中实现,派生类可以任何方式定义成员变量。

答案 2 :(得分:0)

我认为你提到的模板可能是一个不错的选择,所以像:

class MyVarBase
{
};

class MyVar : public MyVarBase
{
  int Foo();
};

template <class T> class MyBase
{
public: 
  MyBase(T* v) : m_var(v) {}
  T* GetVar() { return m_var; }

private:
  T* m_var;
};

class MyClass : public MyBase<MyVar>
{
public:
  MyClass(MyVar* v) : MyBase(v) {}
};

然而,这取决于您实际可以改变的类别。此外,'MyClass'定义可能是多余的,除非它有超出MyBase类的其他成员。