Chap
Chap

Reputation: 3839

Initializing superclass in derived class's constructor

MemRef is a simple class to point to some memory it doesn't own.

Base class:

class MemRef {

protected:
    const char *               _ptr;
    std::size_t                _len;

public:
    // Constructors
    MemRef()                        : _ptr(0), _len(0) {}
    MemRef(const string& s)         : _ptr(s.c_str()), _len(s.length()) {}
    MemRef(const char* b, size_t l) : _ptr(b), _len(l) {}
};

Loaded_MemRef is a subclass that supplies its own buffer, when the caller can't be trusted to supply memory that remains allocated and unchanged for the lifetime of the MemRef. Loaded_MemRef copies the data into a secret buffer it controls, and then points to it, allowing me to treat it like a plain MemRef.

Derived class:

class Loaded_MemRef : public MemRef {

private:
    const string _memory;

public:
    Loaded_MemRef(const string& s) : ??? {}
    Loaded_MemRef(const char*);
    Loaded_MemRef(const char*, const size_t);
};

I'm having trouble creating the ctors for Loaded_MemRef. I must copy into _memory the memory the caller provided first, before calling MemRef's ctor; otherwise MemRef can't retrieve a valid _memory.c_str(). But my understanding is that the call to MemRef(_memory) must come first, before I initialize Loaded_MemRef's members. So I tried this:

Loaded_MemRef(const string& str) :
    MemRef(),            // get this over with
    _memory(str),                  // copy str into _memory
    MemRef::_ptr(_memory.c_str()), // (LINE 108) "reach up into" MemRef and set its protected members
    MemRef::_len(_memory.length())
{}

This complains:

MemRef.cpp: In constructor 'Loaded_MemRef::Loaded_MemRef(const std::string&)':
MemRef.cpp:108: error: expected class-name before '(' token
MemRef.cpp:108: error: expected '{' before '(' token

(Line 108 is indicated above; the next line, setting _len, doesn't get flagged, although perhaps the compiler bailed.)

What is the right way to do this?

Upvotes: 1

Views: 341

Answers (3)

aschepler
aschepler

Reputation: 72483

You could just change _ptr and _len from the body of the Loaded_MemRef constructor (since they're protected).

You could give MemRef a (protected?) member function for changing where it points, and use that from the body of the Loaded_MemRef constructor.

Or if you really, really want/need to initialize the MemRef just once, you can move that string to the one thing that does get initialized before a base class: another base class (declared either virtual or earlier).

Using only C++03:

struct Loaded_MemRef__Base
{
    std::string _memory;
    explicit Loaded_MemRef__Base(const std::string& str)
      : _memory(str) {}
};

class Loaded_MemRef
    : private Loaded_MemRef__Base,
      public MemRef
{
public:
    Loaded_MemRef(const string& str) :
        Loaded_MemRef__Base( str ),
        MemRef( _memory.data(), _memory.length() )
    {}
};

With C++11 features:

struct Loaded_MemRef__Base
{
    std::string _memory;
};

class Loaded_MemRef
    : private Loaded_MemRef__Base,
      public MemRef
{
public:
    Loaded_MemRef(std::string str) :
        Loaded_MemRef__Base{ std::move(str) },
        MemRef( _memory.data(), _memory.length() )
    {}
};

Upvotes: 2

anatolyg
anatolyg

Reputation: 28320

You don't really need to set stuff in the initializers. You can do it in the body of the constructor:

Loaded_MemRef(const string& str) :
    MemRef(),            // get this over with
    _memory(str),        // copy str into _memory
{
    _ptr = _memory.c_str();
    _len = _memory.length();
}

If your base class had const size_t _len, it would be impossible to implement what you want, because const members require initializers, and initialization order mandated by C++ is opposite to what you need.

Upvotes: 2

jdc
jdc

Reputation: 339

You can't initialize the members of the superclass in the derived class initialization list, even if they are protected. The solution is to use a constructor of the superclass.

Upvotes: 1

Related Questions