Base class or derived class: Who should own a derived ptr (to a different class)
Child1 is one of a dozen classes derived from BaseClass. Bar1 is one of a dozen classes derived from TFoo. All objects derived from BaseClass will have a pointer to an object derived from class TFoo.
QUESTION: which is considered better form:
1) To have the base class own a pointer to a base class object (TFoo), and have derived clases (Child1, Child2, etc.) downcast the TFoo pointer to the actual derived type they use and own
OR
2) To have the derived classes (Child1, Child2, etc.) own pointers to derived classes (Bar1, Bar2, etc.) and have a pure virtual function in the base class to get access to an upcast TFoo*?
(examples below are simplified -- not intended to be syntactically perfect)
EXAMPLE 1:
class BaseClass
{
public:
TFoo* GetFoo(void) { return mpFoo; }
protected:
TFoo* mpFoo;
};
class Child1 : public BaseClass
{
public:
Bar1* GetBar1(void) { return (Bar1*)mpFoo; }
};
class Child2 : public BaseClass
{
public:
Bar2* GetBar2(void) { return (Bar2*开发者_StackOverflow社区)mpFoo; }
};
EXAMPLE 2:
class BaseClass
{
public:
virtual TFoo* GetFoo(void) = 0;
};
class Child1 : public BaseClass
{
public:
virtual TFoo* GetFoo(void) { return (TFoo*) mpBar; }
Bar1* GetBar(void) { return mpBar; }
protected:
Bar1* mpBar;
};
class Child2 : public BaseClass
{
public:
virtual TFoo* GetFoo(void) { return (TFoo*) mpBar2; }
Bar2* GetBar(void) { return mpBar2; }
protected:
Bar2* mpBar2;
};
All objects derived from BaseClass will have a pointer to an object derived from class TFoo.
If this truly is a requirement of all derived classes, it is usually better to implement this directly in the base class if possible than it is to require each derived class to implement it.
Another telling point:
Child1 is one of a dozen classes derived from BaseClass.
Think of all the replicated code that your example 2 creates. Given two options, one of which results in lots of replicated code but the other doesn't, I'll almost always choose the one that doesn't have all that replicated code. Each copy represents a place where someone can make a mistake. Moreover, what if you decide to change the meaning of GetFoo()
? One copy = one change. Dozens of copies = dozens of changes.
Casting is error prone and should be avoided when possible. You should consider using method 2, unless efficiency is a primary concern (to avoid a virtual function call). In either case, you can use a template to avoid redundancy:
template <typename Bar>
struct BasicChild : BaseClass {
virtual TFoo* GetFoo() const { return mpBar; }
Bar* GetBar() const { return mpBar; }
protected:
Bar* mpBar;
};
struct Child1 : BasicChild<Bar1> { }
struct Child2 : BasicChild<Bar2> { }
精彩评论