【问题标题】:Manually calling constructor of base class outside initialization list在初始化列表之外手动调用基类的构造函数
【发布时间】:2020-10-07 14:54:04
【问题描述】:

我有一个Derived 类,其构造函数必须填充struct 的字段,该字段作为参数传递给Base 类的构造函数。我希望能够命名我正在填充的结构的字段,以保持我的代码面向未来(即:抵抗MyStruct 成员的添加和/或重新排序)。

注意struct MyStruct 有默认值,因此不能直接在初始化列表中使用命名字段进行初始化(例如:Base({.a = a, .b = b}) 不起作用)。另外,在我的例子中,Base 的复制构造函数被删除了。另外,我使用的是 C++ 11。

我想出的解决方案是使用placement new运算符在this指向的内存上手动调用Base类的构造函数。为了实现这一点,我还必须在我的Base 类中添加一个protected 默认构造函数。这种方法是否有任何可能的缺点和/或有人可以提出更好的方法吗?

#include <iostream>

struct MyStruct
{
        int a = 0;
        int b = 1;
};

class Base
{
public:
        Base(MyStruct str){
                std::cout << "a: " << str.a << ", b: " << str.b << "\n";
        }
        Base(Base&&) = delete; // no copy constructor
protected:
        Base(){ // dummy, does exactly nothing.
                // it only exists to be called by
                // the derived class's constructor
        }
private:
        int amember;
};

class Derived : public Base
{
public:
        Derived(int a, int b)
        {
                MyStruct str;
                str.a = a;
                str.b = b;
                new (this) Base(str);
        }
private:
        int anothermember;
};

int main()
{
        MyStruct str;
        str.a = 10;
        str.b = 20;
        Base b(str);
        Derived d(10, 20);
        return 0;
}

编辑:补充提到 Base 不能被复制,明确指出 Base::Base() 什么都不做。

【问题讨论】:

  • 您在寻找named-arguments吗?
  • 在我想出这个使用 struct 的解决方案之前,我一直在研究这个问题。我认为命名参数是 C++ 真正需要的一个特性,并且提供的解决方案比我希望的要复杂。
  • 第二段似乎没有意义。指定的初始化器将在 C++20 中添加,Base({.a = a, .b = b}) 将是正确的。一些编译器已经支持。
  • 是的,c++20 有望带来这一点,但问题被标记为 c++11。是不是提到“Base 不能被复制”你认为没有意义?我会改写。我的意思是,在这种情况下,Base 类有一个已删除的复制构造函数,在@NathanOliver 的原始答案(现已编辑)假设复制构造函数可用之后,我必须澄清这一点。我现在再澄清一下。
  • Base的拷贝构造函数与此无关

标签: c++ c++11


【解决方案1】:

我想补充一点,如果您出于某种原因不想要命名的辅助函数,这可能是一个使用 IILE(立即调用 Lambda 表达式)的好机会:

class Derived : public Base
{
public:
Derived(int a, int b) : Base{[](){
    MyStruct str;
    str.a = a;
    str.b = b;
    return str; }()}
    {}
};

好处是您将不需要构造类及其成员只需一次,因为您在初始化列表中执行所有操作。如果您将来向 Base 添加重要成员,您将不必为双重初始化付费。

【讨论】:

    【解决方案2】:

    使用辅助函数代替

    class Derived : public Base
    {
    public:
            Derived(int a, int b) : Base(make_mystruct(a, b)), anothermember(some_value) {}
    private:
            int anothermember;
            static MyStruct make_mystruct(int a, int b) { return MyStruct(a, b); }
    };
    

    【讨论】:

    • 谢谢,我应该提到Base 没有复制构造函数。我会更新问题。
    • 当然!非常感谢,看起来很整洁。您能指出我的方法与您的方法相比有哪些缺点吗?
    • @oromoiluig one 我不知道你的代码是否合法,但我知道这段代码是合法的,所以这对我来说是一个加分项。此外,如果您的 Base 类有一个具有可观察到的副作用的析构函数,那么在使用placement new 之前不调用析构函数,您也会有未定义的行为。
    • 关于析构函数的要点。考虑到 Base() 的受保护的默认构造函数完全什么都不做,这不是问题,但它绝对不整洁。
    • @oromoiluig 这些要求没有可能的解决方案,您必须在某个阶段依赖参数顺序。 (直到在 C++20 中添加了指定的初始值设定项)
    猜你喜欢
    • 2021-01-05
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多