2008-11-18 48 views
6

我在某些C++代码中遇到了一些棘手的问题,这些代码很容易用代码描述。我有一个是像类:在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进行模板化。

所有这些选项看起来并不理想,所以我想知道是否有其他人遇到过这样的情况,以及是否有一个好方法使其工作。

回答

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()); 
} 
+0

在这种情况下,我会使用dynamic_cast <>而不是static_cast <>。我认为它更好地捕捉了这个意图。 – coryan 2008-11-18 12:46:12

2

不知道更多的背景下,很难肯定地说,但我会重新考虑是否需要这一类层次结构首先。你真的需要MyVarBase和MyBase类吗?你可以摆脱构图而不是继承?也许你根本不需要类的层次结构,如果你对与上述类一起工作的函数和类进行模板化。也许CRTP也可以提供帮助。在C++中有很多使用虚函数的替代方法(在一定程度上,一般情况下还有继承)。

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

或者,您可能能够从基类中移除该变量。使GetVar()函数为纯虚拟的,所以它只能在派生类中实现,派生类可以用任何他们喜欢的方式定义成员变量。

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类中有其他成员。