在构造函数代码之前禁用默认类成员初始化

Posted

技术标签:

【中文标题】在构造函数代码之前禁用默认类成员初始化【英文标题】:Disable default class member initialization before constructor code 【发布时间】:2018-12-28 18:15:24 【问题描述】:

在 C++ 中,在成员初始化列表中构造的类的任何成员都默认在包含类的构造函数执行之前构造。但是,如果该成员变量无论如何在它所在的类的构造函数中构造,这似乎是非常浪费的。

我在下面提供了一个示例来阐明我的意思。这里,Example 类有一个LargeIntimidatingClass 类型的成员变量x。使用成员初始化列表(Example 中的第一个构造函数)x 只构造一次。但是,如果x不能使用成员初始化列表合理构造,它会被构造两次!

//This class used as part of the example class further below
class LargeIntimidatingClass 
    // ...
    //many member variables and functions
    // ...

    LargeIntimidatingClass() 
        //Painfully expensive default initializer
    

    LargeIntimidatingClass(int a, double b) 
        //Complicated calculations involving a and b
    
;

//Here, this class has a LargeIntimidatingClass as a member variable.
class Example 
    LargeIntimidatingClass x;
    char c;

    //Basic member initialization list constructor. Efficient!
    Example(int a, double b, char c) : x(a,b), c(c) 

    //What if the parameters to the LargeIntimidatingClass's constructor
    //need to be computed inside the Example's constructor beforehand?
    Example(std::string sophisticatedArgument) 
        //Oh no! x has already been default initialized (unnecessarily!)

        int a = something1(sophisticatedArgument);
        double b = something2(sophisticatedArgument);
        //x gets constructed again! Previous (default) x is totally wasted!
        x = LargeIntimidatingClass(a,b);

        c = something3(sophisticatedArgument);
    
;

是的,我意识到在这个愚蠢的例子中你可以写Example(string s) : x(f1(s),f2(s)), c(f3(s)) ,但我相信你可以想象将一堆逻辑塞进成员初始化列表的情况很麻烦(甚至是不可能的)。

在成员初始化列表中没有列出成员变量的默认构造函数时,是否可以禁用它?

【问题讨论】:

【参考方案1】:

您不能禁用构造。在到达构造函数的主体之前,必须初始化所有类成员。也就是说,您可以轻松解决该问题。您可以添加一个私有静态成员函数,该函数获取 ab 并从中返回 LargeIntimidatingClass

class Example 
    LargeIntimidatingClass x;
    char c;
    static LargeIntimidatingClass make_LargeIntimidatingClass(std::string sophisticatedArgument)
    
        int a = something1(sophisticatedArgument);
        double b = something2(sophisticatedArgument);
        return LargeIntimidatingClass(a,b);
    
    static char make_c(std::string sophisticatedArgument)
    
        return something3(sophisticatedArgument);
    
public:

    //Basic member initialization list constructor. Efficient!
    Example(int a, double b, char c) : x(a,b), c(c) 

    // now we use helpers to initialize in the member initialization list
    Example(std::string sophisticatedArgument) : x(make_LargeIntimidatingClass(sophisticatedArgument), c(make_c(sophisticatedArgument) 
        //now everything is initialized correctly
    
;

【讨论】:

【参考方案2】:

禁用语言工作方式的一个组成部分?不可以。但是您可以重构以使用该语言,或者以各种方式绕过它。

    拥有一个指向扩展类的(智能)指针成员。 将成员设为std:aligned_storage 并通过放置新创建对象。然后自己仔细管理对象的生命周期。 保持std:optional。管理初始化并让库类型管理其余部分,以减少对象大小的一些开销。

【讨论】:

【参考方案3】:

在成员初始化列表中没有列出成员变量的默认构造函数时,是否可以禁用它?

不,这是不可能的。

【讨论】:

以上是关于在构造函数代码之前禁用默认类成员初始化的主要内容,如果未能解决你的问题,请参考以下文章

静态构造函数, 静态成员初始化/调用顺序

在 C++ 中使用非默认构造函数初始化对象的成员类

Kotlin类的初始化 ② ( 主构造函数 | 主构造函数定义临时变量 | 主构造函数中定义成员属性 | 次构造函数 | 构造函数默认参数 )

为什么 没有缺省构造函数的类类型成员 必需要在初始化列表 里初始化 ?

构造函数和析构函数

C++中如何在子类的构造函数中调用基类的构造函数来初始化基类成员变量