views:

240

answers:

5

Here are my classes:

ParentClass, ParentObj

DerivedClass (inherits from ParentClass), DerivedObj (inherits from ParentObj).

ParentClass has a protected member:

std::vector< ParentObj* >

DerivedClass allocates only DerivedObj* objects to this vector.

Problem is:

When I use ParentClass, I want to access its vector objects with an iterator of type:

std::vector< ParentObj* >::const_iterator

And when I use DerivedClass, I want to access its vector objects with an iterator of type:

std::vector< DerivedObj* >::const_iterator

How can I make it work?

+1  A: 

std::vector< ParentObj* > and std::vector< DerivedObj* > are two totally different types hence you can not implicitly convert one to another. What you can do is convert each ParentObj* to DerivedObj* using dynamic_cast. But generally it is not considered a good practice.

Naveen
I know, that's what I do now... No other way?I thought of having vector<ParentObj*> a private member and DerivedClass will have a private member of its own: vector<DerivedObj*>, but this is also not very elegant...
Jack Skellington
I think the better practice here is to add a virtual function on the objects that does what is needed. Thus call it on the ParentObj* and runtie will choose to call the function defined on ParentObj or DerivedObj
Mark
Interesting idea.> virtual void derivedObjDoesSomething() { assert(0); }I'll think about it, thanks.
Jack Skellington
+1  A: 

You can't do that using basic C++ inheritance. The reason is that the compiler doesn't know that the vector contains only pointers to DerivedObj when used in DerivedClass. I second Naveens statement about dynamic cast.

You could use two vectors std::vector< ParentObj* > mParent and std::vector< DerivedObj* > mDerived and a virtual Add() function. The base implementation would add only to mParent. The Add function in DerivedClass would also add to mDerived.

ParentClass would have a GetParentIterator() function and DerivedClass would have another (additional) function GetDerivedIterator().

ur
+4  A: 

You can use a virtual override in your DerivedClass class returning a subtype of the ParentClass return type...

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) );
    }
 };

Both functions are the same 'vtable' entry, though their return type differs.

Alternatively, you may use the same technique to create 'inherited' iterators.

Next to that, it's not a bad idea to have a vector of DerivedObj*'s in the DerivedClass object, as long as you guarantee that they are also present in the ParentClass's vector.

xtofl
+1, providing get() method looks nice and restricts casting to only this function.
Naveen
That's a good idea if I don't work with the vector's iterators...By the way, I didn't know you can overload a method with a different return value, interesting.
Jack Skellington
If the class has suitable invariants (for instance, pointers are only added to the vector via a virtual member function overridden in DerivedClass), you could make that a static_cast. Type erasure, basically.
Steve Jessop
Yeah, I know. It still doesn't sound like a good code practice (and it appears in a lot of places in the code).
Jack Skellington
@Jack, returning a subtype of the original return type in an overridden virtual method is referred to as a covariant return type. It isn't overloading as such.
jon hanson
You can override if the overriding type is derived from the overriden... however since this is not true for the iterators it won't work that well :/
Matthieu M.
A: 

Your inheritance design is wrong.

DerivedClass must not inherit from ParentClass. ParentClass has a protected member std::vector< ParentObj* >. And DerivedClass must have another protected member std::vector< DerivedObj* >.

You may create template class

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

struct ParentClass : MyClass <ParentObj> {};
struct DerivedClass: MyClass <DerivedObj> {};
Alexey Malistov
What does it change ? Why a protected member anyway, that beats the purpose of encapsulation...
Matthieu M.
How is this any different from struct ParentClass {std::vector<ParentObj*> m_objects;}; and struct DerivedClass {std::vector<DerivedObj*> m_objects;}; I.e. two unrelated classes?
jon hanson
Yes! unrelated classes. It is better than inheritance. If you want to have common functions you have to make a common base class.
Alexey Malistov
+2  A: 

I have a question there: Why does DerivedClass inherits from ParentClass ?

Do you need a polymorphic behavior, or do you want to reuse the implementation of ParentClass ?

Inheritance is often badly misused. Really.

Then there is the (typical) problem of having a container class, and how to expose its elements. Unfortunately the iterators are (despite all their goods) poorly supported by the language unlike the pointers they are supposed to emulate.

So, a reminder: Inheritance is a is-a relationship, for code reuse there is Composition.

Here you could perfectly write a template class that would play the part of the container and provide common methods.

Then, for the problem of exposition... you can write your own iterators, with the correct base-derived relationship, or you can opt to preselect a number of algorithms (sort, foreach, erase_if) that would work with user-supplied predicates.

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;
};

Then, on to your classes:

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;
};

Try to separate polymorphism from code reuse, and the problem should get simpler.

Matthieu M.