1

Take two classes that both can only be created by new. One class is the base and the other is a derivative. The derived class only adds methods.

class Base
{};

class Derived : public Base
{};

Base * b = new Base{}
Derived * d = covert( b );

// - or -

Base * b = new Base{};
convert( b ); // converts Base to Derived
Derived * d = dynamic_cast<Derived *>(b);

What I want to do is take the Base class data that has been allocated and extend/wrap with the derivative via some method or function, convert.

Update: Building for embedded systems memory is scarce, so I am doing everything I can to reduce that amount of memory allocation. I was just wondering if there was a way to just sort of extend the base class already allocated memory and wrap it with the derivative.

More Updates: Although the embedded system is ARM and I am currently using LLVM compiler this might not be true in the future. So a standard compliant way is preferred.

NebulaFox
  • 7,813
  • 9
  • 47
  • 65

7 Answers7

2

Promoting classes is not possible in C++. What you might want to do is - like you said - wrap Base in Derived:

class BaseInterface { ... };

class DerivedInterface: public BaseInterface { ... };

class Base: public BaseInterface { ... };

class Derived: public DerivedInterface {
private:
  Base* base;
public:
  Derived(Base* useBase): base(useBase) {}
  ~Derived() { delete base; }
  // implement using base
};

And use it like that:

Base* object = new Base(...);
// use object with base functionality
object = new Derived(object);
// use object with derived functionality
delete object; // free both base and derived memory
Antoine
  • 13,494
  • 6
  • 40
  • 52
2

If I understand correctly your question one possible solution is to use aggregation instead of inheritance.

class Base
{/*has only the data*/}

class Derived 
{
    Base &base;
    Derived(Base &b) : base(b) {}
    //now methods from derived will use the data members from instance passed in constructor
    //if is possible the Derived needs to be a friend class of Base in case there are no getter for all members
}

Instead of reference we can use a smart pointer if necessary. This way you avoid the cast by constructing a new Derived object which uses the data from the a Base object.

Marius
  • 833
  • 5
  • 11
1

The derived class only adds methods.

Then there is no point to that class. At all.

You can use deriving to:

  • override virtual methods (and thus customize behaviors) => Good
  • extend the data of the base class => Bad, code reuse should use composition, still it works

In your particular case ? It's just useless. It does not bring anything to the table. If you wish to add new functionalities to your base class, then implement free functions. And as a bonus, getting rid of the derived class, you'll also get rid of the need to cast.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • @NebulaFox: In the worst case, we are talking about *Composition*. You could perfectly build a `class Specialized { public: /* methods */ private: Base* _base: };` and use that on the stack, it would work well, takes just as much space as a pointer, and "hide" the base class if that's what you meant (not sure, didn't understand what you meant unfortunately). If you would bring a concrete example of why you think you need a derived class, then I could give a concrete explanation of why you don't ;) – Matthieu M. Oct 19 '12 at 13:30
  • An example from another type of language is Smalltalk with it's use of categories. A C++ example is class Data. This class has simple methods for accessing data, saving and loading. And say saving and loading takes a block or a function processing the data. The class Data is then split off into different classes such as Music and Image. These classes add methods that look like they override saving and loading but are helper methods with the special block or function for processing. – NebulaFox Oct 19 '12 at 13:45
  • 1
    @NebulaFox: but Smalltalk does not have free functions. In C++ you can add functions *outside* of classes. – Matthieu M. Oct 19 '12 at 15:14
  • but I am encapsulating logic so I do not want *free functions*. What about my C++ example? – NebulaFox Oct 19 '12 at 15:49
  • @NebulaFox: do you mean the answer you just gave ? I would say that deleting the content of `base` is slightly evil. As for the functionality, what's wrong with the `Specialized` implementation I proposed above ? – Matthieu M. Oct 19 '12 at 17:29
1

A potential option comes about thanks to C++11

class Base
{
  pubilc:
    Data data;

    Base( Base && base ) : data( std::move( base.data ) ) {} 
};

class Derived : public Base
{
  pubilc:
    static Derived * convert( Base *& base )
    {
        Derived * d = new Derived{ std::move( *base ) };
        delete base;

        base = d;
        return d;
    }
 };

Although it is not what I hoped for memory-wise it is only a small hit, as far as I am aware. By using the move construct, the only overhead is the creation of a new Derived object before deleting the old Base object.

There should be a check before anything is done, but this will do for now.

NebulaFox
  • 7,813
  • 9
  • 47
  • 65
0

You could add a helper class which is a friend of the base class, and add extra functions to that, e.g.

class Base 
{
   friend class BaseHelper;
   // ....
};
class BaseHelper
{
    public:
    // all functions can access data into Base objects:
    void f(const Base& b) const; // can only read data in b, not change it
    void g(Base& b); // can change data in b
}
Chris Card
  • 3,216
  • 20
  • 15
0
Derived *d =static_cast<Derived *>(b); //? :-/

Here is one explanation

Another explanation

Much more detailed explanation of what's happening under the hood

UPDATE:

Stack Overflow - Downcasting using the static_cast in c++

Downcasting in C++ using static_cast

SPECIFICATION:

An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cvqualification as, or greater cvqualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

FOR LLVM ONLY:

http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html - ALLOWS DOWNCASTING(is not undefined behavior, since it doesn't use C++ RTTI).

There is no standard-compliant way of downcasting:

class Parent{
    public: void gotoWork(){ cout << doWork(); }
};

class Child: public Parent{
     public: void gotoSchool(){ cout << attendClasses();}
};

Parent *p = new Parent();
Child *ch = static_cast<Child*> p;
//now child has to go work? :-(

EDIT

Apart from all other ways, why not put two objects? one of type "Base" and another of type "Derived" into a UNION?

PS: Its language-specification conformant.

Community
  • 1
  • 1
Aniket Inge
  • 25,375
  • 5
  • 50
  • 78
  • Thank you for the links, but they do not explain the particular case. Your first link explains what `static_cast` is which is fair enough for anyone else reading this. The second link doesn't add much. But the third link has the gold showing you particular example. – NebulaFox Oct 19 '12 at 12:48
  • One more thing, when you do the `static_cast` all methods will work on the `Derived` object `d`? – NebulaFox Oct 19 '12 at 12:56
  • 1
    Isn't that undefined behaviour? – Chris Card Oct 19 '12 at 12:59
  • It is an undefined behavior. But works on all compilers. Its undefined because C++ specification doesn't talk about it. Works on all compilers equally. – Aniket Inge Oct 19 '12 at 13:00
  • 1
    @PrototypeStark: **If** the rvalue of type “pointer to cv1 `B`” points to a `B` that **is actually a subobject of an object of type `D`**, the resulting pointer points to the enclosing object of type `D`. **Otherwise, the result of the cast is undefined.** I think that the specification is pretty clear... Just because it's working now means nothing, gcc 4.7 broke a lot of code that worked with gcc 4.6 by being more aggressive in its exploitation of the undefined behavior rules. – Matthieu M. Oct 19 '12 at 13:19
  • @PrototypeStark I think MatthieuM. is pointing at is, that it may work now but there is nothing to say it will continue to work. So a more standard way of doing things at least guarantees that it will not suddenly break – NebulaFox Oct 19 '12 at 13:27
  • @PrototypeStark: Yes. Recommanding taking advantage of undefined behavior is like recommanding playing with fire. You may not have burned yourself yet, but it's still a dangerous game. – Matthieu M. Oct 19 '12 at 13:31
  • @NebulaFox since you mentioned Embedded System, what is the microprocessor/microcontroller you're using and what compiler? – Aniket Inge Oct 19 '12 at 13:36
  • the processor is ARM and I am allowed to use LLVM – NebulaFox Oct 19 '12 at 13:46
  • 1
    @NebulaFox, LLVM doesn't follow C++'s RTTI. Is more flexible and allows you to downcast this way. :-). http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html – Aniket Inge Oct 19 '12 at 13:54
  • @people-downcasting Please mention WHY you're downcasting anything. If you're a language Nazi go elsewhere. We are here to solve REAL WORLD problems not be language conformant. – Aniket Inge Oct 19 '12 at 13:55
  • @NebulaFox people here seem to be ignorant downvoters. Why don't you modify the question to state explicitly the compiler and processor in use. while I change my answer to include links specifically for LLVM. Or else this "accepted" answer will end up with -ve votes. – Aniket Inge Oct 19 '12 at 14:14
  • The problem is the code may be moved to another embedded system that might not get benefits of LLVM annoyingly. I will un-accept this answer. Thanks for everything though. – NebulaFox Oct 19 '12 at 14:20
  • @NebulaFox Unbelievable. Why aren't they using LLVM on another embedded system? – Aniket Inge Oct 19 '12 at 14:22
  • @NebulaFox there is no standard-complaint downcast available because its a breach of basic object oriented programming. – Aniket Inge Oct 19 '12 at 14:28
  • @PrototypeStark there is an embedded system that only uses gcc. Not everyone has gotten on the LLVM bandwagon. Seriously sucks. – NebulaFox Oct 19 '12 at 14:29
  • @NebulaFox here is one for GCC: http://stackoverflow.com/questions/1358143/downcasting-shared-ptrbase-to-shared-ptrderived – Aniket Inge Oct 19 '12 at 14:35
  • @PrototypeStark: class B { int a, &b; B(): a(1), b(a) {} }; class D: public B { int c; D(): c(2) {} int get() { return b+c; } }; I didn't downvote and I hope I'm not too ignorant, but how exactly exactly is your static_cast going to report `d->get() == 3` without memory violation?? – Antoine Oct 19 '12 at 14:43
  • @Antoine do you understand that you need to create an OBJECT of type D for the constructor to be called? Here he isn't trying to CREATE AN OBJECT OF D, he is just trying to call some methods in D as well as B and SQUEEZE the object, much like what unions do, he is extending the object, NOT CREATING ANOTHER. – Aniket Inge Oct 19 '12 at 14:53
  • 1
    @NebulaFox answering Antoine, I got an idea. Why don't you put two objects, one of type Base and another of type Derived into a union? You get a squeezed object anyway? – Aniket Inge Oct 19 '12 at 14:57
  • @PrototypeStark that's interesting – NebulaFox Oct 19 '12 at 15:00
  • and for all the specifications nazi, its standard compliant. – Aniket Inge Oct 19 '12 at 15:02
  • @PrototypeStark: Yes. The problem here is that since D may be bigger than B, static_cast may be forced to realloc the object to another location, in which case all logic assuming an object has const address is void (int &b in my code). If D has no data and there are no virtual methods, you should do `(Derived*)(void*)b` instead of `static_cast(b)` since you can't be sure static_cast will not change the address. – Antoine Oct 19 '12 at 15:07
  • I am throwing this in for good measure http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=556 – NebulaFox Oct 19 '12 at 15:07
0

Looking through the many answers that I have, the common consensus is that I should do what I said and actually wrap my Base class within the Derived class. The reason I was not doing this is I would lose a particular behaviour you get with inheritance. This behaviour is setting a pointer of the Derived class to a pointer of type Base, which will happen after I did some sort of conversion that will make the Base class transform into the Derived class.

A did stumble on a solution of my own - although apparently it may not compile - and have had many other good ideas as well. In the end, I was a bit slow to realise that because, for my case, my Derived class does not override any virtual methods there was no benefit of using inheritance. The best way is indeed to to use composition.

class Base
{
    // some nice implementation
};

class Derived
{
  public:
    Derived( Base && base )
    {
        this->_base = base;
    }

    Base & base()
    {
        return _base;
    }

  private:
    Base _base;
};
Community
  • 1
  • 1
NebulaFox
  • 7,813
  • 9
  • 47
  • 65