Search code examples
c++c++11derived

Initialization during a pointer cast?


If I define a derived class which has initializers, but end up using pointer casts (i.e. static_pointer_cast), how can I get the initializers to be performed without performing dereference and then an object copy?

#include <string>
#include <memory>

class Base {
public:
    std::string Name;
};

class Derived : public Base {
public:
    std::string Address = "Initialized";
};


int main() {
    auto b_ptr = std::make_shared<Base>();
    b_ptr->Name = "Fred";

    auto d_ptr = std::static_pointer_cast<Derived>(b_ptr);

    fprintf( stdout, "Name: [%s]   Address: [%s]", 
        d_ptr->Name.c_str(),  
        d_ptr->Address.c_str() );  // Address not valid!
}

Link to code: http://coliru.stacked-crooked.com/a/09f2240abff1556b

What is the proper way to handle this?

EDIT: Below is some sample code (much simplified over the real thing, of course) that better illustrates what I am trying to do, and why it would be nice if there was a way to somehow make this work.

Updated Sample Code: http://coliru.stacked-crooked.com/a/cdcc31a4417bb52b

In this example, I have two data sources, one is from a source system and the other is what is used internally. I don't really want to copy all the data, just layer on some additional information to it.

I am not sure about using std::move (as suggested by @Mooing Duck) because of the consequences it has with the source data....something I need to explore more. But from this example, the benefit of not performing a copy is clear, and having to use a "has-a" style implementation makes the subsequent object usage awkward. I.e.:

test_row->Values[0].c_str()   and 
test_row->RowTotal

would become:

test_row->row->Values[0]    yet still
test_row->RowTotal

Perhaps I am looking at this all wrong and there is a better algorithm for doing this?


Solution

  • You can give Derived a Base&& constructor, and use it to move (shallow-copy) the contents of the Base into the derived, which leaves the origional Base in an "empty" state.

    class Base {
    public:
        std::string Name;
    
        //Note: The compiler is generating these invisibly for you:
        //Base() :Name() {}
        //~Base() {}
        //Base(const Base& r) : Name(r.Name) {}
        //Base(Base&& r) noexcept : Name(std::move(r.Name)) {}
        //Base& operator=(const Base& r) : Name(r.Name) {}
        //Base& operator=(Base&& r) noexcept : Name(std::move(r.Name)) {}
    };
    
    class Derived : public Base {
    public:
        std::string Address = "Initialized";
        Derived() = default;
        Derived(Base&& b) : Base(std::move(b)) {}
    };    
    
    int main() {
        auto b_ptr = std::make_shared<Base>();
        b_ptr->Name = "Fred";
    
        auto d_ptr = std::make_shared<Derived>(std::move(*b_ptr));
        //NOTE AT THIS POINT b_ptr POINTS TO A BASE WHOS Name IS EMPTY
        b_ptr.reset(); //reset to prevent accidental errors with lack of content
    
        fprintf( stdout, "Name: [%s]   Address: [%s]", 
            d_ptr->Name.c_str(),  
            d_ptr->Address.c_str() );  // Address not valid!
    }
    

    See it working here: http://coliru.stacked-crooked.com/a/f3a6062f6c459c7c
    And also see proof of movement here: http://coliru.stacked-crooked.com/a/f7f6cc4aa06d2746

    However, it's worth noting that I can't think of a good reason you could ever want to do this. It sounds like your code is poorly designed.