在派生类的构造函数中初始化超类

Posted

技术标签:

【中文标题】在派生类的构造函数中初始化超类【英文标题】:Initializing superclass in derived class's constructor 【发布时间】:2013-11-27 22:18:44 【问题描述】:

MemRef 是一个简单的类,用于指向它不拥有的一些内存。

基类:

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 是一个子类,它提供自己的缓冲区,当不能信任调用者提供在 MemRef 的生命周期内保持分配和不变的内存时。 Loaded_MemRef 将数据复制到它控制的秘密缓冲区中,然后指向它,让我可以将其视为普通的 MemRef

派生类:

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

我在为 Loaded_MemRef 创建 ctor 时遇到问题。在调用 MemRef 的 ctor 之前,我必须将调用者 首先 提供的内存复制到 _memory;否则 MemRef 无法检索到有效的 _memory.c_str()。但我的理解是,在初始化 Loaded_MemRef 的成员之前,必须先调用 MemRef(_memory)。所以我尝试了这个:

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())

这抱怨:

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

(上面指出了第 108 行;下一行,设置 _len,不会被标记,尽管编译器可能会被保释。)

这样做的正确方法是什么?

【问题讨论】:

为什么Loaded_MemRef会有这么奇怪的构造函数? @KerrekSB:一个用于字符串引用,一个用于空终止 c 字符串,一个用于缓冲区(无空终止,需要长度)。 【参考方案1】:

你真的不需要在初始化器中设置东西。您可以在构造函数的主体中执行此操作:

Loaded_MemRef(const string& str) :
    MemRef(),            // get this over with
    _memory(str),        // copy str into _memory

    _ptr = _memory.c_str();
    _len = _memory.length();

如果你的基类有const size_t _len,就不可能实现你想要的,因为const成员需要初始化器,而C++强制的初始化顺序与你需要的相反。

【讨论】:

你所说的 const _len 是否也适用于 const _ptr 是的;但是您的_ptr 不是那种 那种const(这不是问题) - 有问题的是const char *const _ptr【参考方案2】:

您可以从Loaded_MemRef 构造函数的主体中更改_ptr_len(因为它们是protected)。

您可以给 MemRef 一个 (protected?) 成员函数来更改它指向的位置,并从 Loaded_MemRef 构造函数的主体中使用它。

或者,如果您真的、真的想要/需要只初始化一次 MemRef,您可以将 string 移至在基类之前初始化的一件事:另一个基类(声明为 virtual或更早)。

仅使用 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() )
    
;

具有 C++11 特性:

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

【讨论】:

使用 C++11 的 std::move() 的好例子。我不清楚是否需要您的大括号,或者是否可以同样使用括号? 需要大括号,因为 C++11 示例没有为 struct 定义构造函数。括号表示“使用这些参数调用构造函数”;大括号表示“使用这些值初始化结构(或 std::initializer_list)的成员”。【参考方案3】:

您不能在派生类初始化列表中初始化超类的成员,即使它们是受保护的。解决方案是使用超类的构造函数。

【讨论】:

以上是关于在派生类的构造函数中初始化超类的主要内容,如果未能解决你的问题,请参考以下文章

Java基础之类的特性和接口

如何在 C++ 子类的构造函数中初始化超类的 const 成员变量?

在派生类的构造函数中初始化没有默认构造函数的基类

C++基类和派生类的构造函数

生成一个派生类对象时,调用基类和派生类构造函数按啥次序

阿里笔试题-派生类构造函数 创建顺序