如何在不调用C++中的构造函数的情况下创建对象



以下程序在5个文件中。输出是0 32,而不是14 32,因此在不调用构造函数的情况下构造对象。这怎么可能?

character.h:

#ifndef CHARACTER_H
#define CHARACTER_H 
class Character
{
public:
    Character() {}
    class Settings
    {
    public:
        int size_;
        Settings():
            size_(14)
        {}
    };
    const static Settings DEFAULT_SETTINGS;
};
#endif // CHARACTER_H

character.cpp:

#include "character.h"
const Character::Settings Character::DEFAULT_SETTINGS;

word.h

#ifndef WORD_H
#define WORD_H
#include <iostream>
#include "character.h"
class Word 
{
public:
    Word() {}
    class Settings
    {
    public:
        Character::Settings characterSettings_;
        int length_;
        Settings():
            length_(32)
        {
            characterSettings_ = Character::DEFAULT_SETTINGS;
        }
    };
    static const Settings DEFAULT_SETTINGS;
    void write(Settings settings = DEFAULT_SETTINGS) // this default parameter is 
                                                     // constructed without a   
                                                     // constructor call
    {
        std::cout << settings.characterSettings_.size_ << std::endl;
        std::cout << settings.length_ << std::endl;
     }
 };
#endif // WORD_H

word.cpp

#include "word.h"
const Word::Settings Word::DEFAULT_SETTINGS;

main.cpp

#include "word.h"
int main(int argc, char *argv[])
{
    Word member;
    member.write();
    return 1;
}

这被称为静态初始化失败。对于在不同转换单元中定义的具有静态持续时间的变量,构造函数的执行基本上没有固定的顺序。在这种特定情况下,Word::DEFAULT_SETTINGS已经在Character::DEFAULT_SETTINGS之前构造,并且因此已经读取了静态持续时间的变量在实际初始化之前具有的0值。如果你想看到一些有趣的东西,转储Character::DEFAULT_SETTINGS的内容,你会发现奇怪地它是14

静态初始化顺序问题可以通过一些巧妙的(并非真正的)头文件技巧来解决。

基本上,这个想法的要点是这样的(我已经在实践中实现了好几次,尽管是在亿万年前,所以这不仅仅是目前的一些空闲堆栈溢出想法):

基本上,您将静态对象(如foo_class g_foo;)的定义放在foo.h头文件的一个特殊分隔区域中:

#ifndef FOO_H
#define FOO_H
#include "bar.h"  // dependency: crucial part!
// ... declares foo_class ...
// foo is a client of bar
// Now somewhere near the bottom:
#ifdef DEFINE_GLOBAL_SINGLETONS
foo_class g_foo;
#endif

现在,您设置了一个专门指定的singleton存储库源文件,其中包含了所有的头文件。

// singletons.cc
#define DEFINE_GLOBAL_SINGLETONS
#include "foo.h"
#include "bar.h"  // note deliberately wrong order!

头文件包含依赖项和保护将导致头被包括在模块依赖项顺序中,因此DEFINE_GLOBAL_SINGLETONS节将以正确的顺序添加到翻译单元中。

在单个翻译单元中,C++要求从上到下构造对象。

因此,您可以获得类似Ada/Modula的模块初始化顺序:在用户之前使用模块。foo用户bar和soo的bar单例(如果有的话)在foo单例之前被初始化。

现在的缺点是:每次头更改时都要重新编译singleton存储库。

相关内容

最新更新