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

时间:2012-03-21 02:34:28

标签: 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;
}

2 个答案:

答案 0 :(得分:6)

这称为静态初始化惨败。基本上,对于具有在不同转换单元中定义的静态持续时间的变量,构造函数的执行没有固定顺序。在这种特殊情况下,Word::DEFAULT_SETTINGS已在Character::DEFAULT_SETTINGS之前构建,因此在实际初始化之前已读取静态持续时间变量的0值。如果你想看到有趣的内容,请转储Character::DEFAULT_SETTINGS的内容,你会看到奇怪足够14

答案 1 :(得分:1)

静态初始化顺序问题可以通过一些聪明的(不是真的)头文件技巧来解决。

基本上这个想法的要点就是这个(我已经在实践中实现过了好几次,虽然很久以前,所以这不仅仅是暂时的Stack Overflow想法):

基本上,您将静态对象的定义(如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

现在,您可以设置一个特别指定的单一存储库源文件,其中包含所有标题。

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

头文件包含依赖关系,并且防护将导致标头包含在模块依赖关系顺序中,因此DEFINE_GLOBAL_SINGLETONS部分将以正确的顺序添加到翻译单元。

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

所以你得到Ada / Modula之类的模块初始化顺序:在用户之前使用模块。 foo用户bar以及bar单例(如果有)在foo之前初始化。

现在的缺点是:每次标题更改时重新编译单例存储库。