C++ 构造函数排序(全局范围)问题

C++ 构造函数排序(全局范围)问题,c++,constructor,global,C++,Constructor,Global,我有一个构造器排序问题,我正试图想出创造性的方法来解决 基本上,我有一个简单的类Color,它存储RGB颜色信息,并允许对所述颜色进行操作并转换为其他颜色空间(24位、16位、4位、HSV、XYZ、LAB等)。这个班本身工作得很好 我还有一个预定义颜色库,例如: namespace Colors { const Color Snow (255,250,250); const Color GhostWhite (248,248

我有一个构造器排序问题,我正试图想出创造性的方法来解决

基本上,我有一个简单的类
Color
,它存储RGB颜色信息,并允许对所述颜色进行操作并转换为其他颜色空间(24位、16位、4位、HSV、XYZ、LAB等)。这个班本身工作得很好

我还有一个预定义颜色库,例如:

namespace Colors {
    const Color Snow                  (255,250,250);
    const Color GhostWhite            (248,248,255);
    const Color WhiteSmoke            (245,245,245);
    const Color Gainsboro             (220,220,220);
    const Color FloralWhite           (255,250,240);
    const Color OldLace               (253,245,230);
    const Color Linen                 (250,240,230);
    const Color AntiqueWhite          (250,235,215);
    const Color PapayaWhip            (255,239,213);
    const Color BlanchedAlmond        (255,235,205);
};
它们在程序中正常使用时也能正常工作

当我试图将构造函数中的那些库颜色用于另一个对象时,我的问题就出现了。毫无疑问,我使用的库颜色的构造器已执行,颜色数据已分配(它会进行一些小的预处理,以计算一些不同的颜色空间值)在另一个类的构造函数之前,该类接收
Color
对象并将其分配给自身内部的存储变量

例如,颜色类有一个构造函数:

Color(const Color &c) {
    setColor(c.getRed(), c.getGreen(), c.getBlue());
}
=
运算符:

Color &Color::operator=(const Color &rhs) {
    setColor(rhs.getRed(), rhs.getGreen(), rhs.getBlue());
    return *this;
}
setColor()
只是一个小辅助函数,用于存储值并预先计算一些颜色空间可选值

当我将一个包含在另一个对象的构造函数中时,请说:

Color _storeColor;
TestClass(const Color &c) {
    _storeColor = c;
}
或:

与:

分配的颜色数据(几乎总是)全部
0
,就好像
Color
类的构造函数还没有运行一样,我完全明白了这一点

因此,我正在寻找关于如何创建预定义颜色库的想法,以便全球范围内的其他构造器可以使用这些颜色库

顺便说一句,类似于:

TestClass myTest(Color(245,245,245));

工作完美,但我不希望有几百个(而且是几百个)或<代码> >定义颜色库的< /Cord>宏,因为这会导致大量不必要的对象复制,并且我希望在每次重新使用颜色时始终保持引用相同的全局实例。

< P> C++标准没有定义不同翻译单元中的构造函数被调用的顺序,如你所知

< >但是大多数C++实现通常提供了指定构造函数初始化顺序的方法,这可能会对您有利。

例如,gcc具有一个属性,您可以将该属性附加到构造函数并控制该构造函数相对于其他构造函数的初始化顺序。这可能是海湾合作委员会的答案

查看您的编译器文档,以获取有关它在该区域中提供的编译器特定功能的更多信息


对于更具可移植性的方法,可以在命名空间范围内,在非平凡类实例之前初始化pod。也许可以利用这一点,以便在这里提出一些方法,但我建议首先研究编译器的能力。利用编译器提供的附加功能没有什么错。

如果可以使用
C++11
,也可以尝试安排预处理,以便在编译时计算预处理,并使颜色定义成为
constepr

class Color {
    public:
        int m_r, m_g, m_b;
        constexpr Color(int r, int g, int b) : m_r(r), m_g(g), m_b(b) {}
        constexpr Color(Color const& o) : m_r(o.m_r), m_g(o.m_g), m_b(o.m_b) {}
};

constexpr const Color RED = Color(255,0,0);
constexpr const Color BLUE = Color(0,255,0);
constexpr const Color GREEN = Color(0,0,255);

这应该保证在调用其他构造函数之前初始化颜色(在动态初始化之前进行常量初始化)。它还有一个额外的好处,即编译可以在编译时执行预处理,因此它甚至可能更高效一些(但是,这也意味着如果预处理只依赖于运行时可用的值,那么它将不起作用)

您遇到的有时被称为“静态初始化顺序失败”

处理此问题的方法之一是使用首次使用时构造的习惯用法,例如通过将颜色定义更改为getter函数来实现:

const Color & Snow(void) {
    static Color snow(255,250,250);
    return snow;
}
您可以在中阅读更多内容


编辑: 要避免代码过多,只需定义一个帮助器宏:

#define DEF_COLOR(name, r, g, b) \
const Color & name(void) { \
    static Color name(r,g,b); \
    return name; \
}

DEF_COLOR(Snow,       255,250,250)
DEF_COLOR(GhostWhite, 248,248,255)
// ...

这工作得很好,但是这个项目是一个库,其他人可以将其合并到他们的代码中,我不能保证他们将使用的编译器。他们中的大多数都是基于GCC,但是什么版本和它支持什么是任何人的猜测。C++ ABI是依赖于编译器的。如果您使用gcc构建库,那么它只能由相同版本的gcc可靠地使用。gcc的二进制ABI保证通常(但并非总是)仅向前兼容。而且,在任何情况下,用GCC构建的任何C++库都不可能与另一个编译器构建的代码链接。“库”作为源代码分发,并由最终用户交叉编译。它可以在多种不同的操作系统上使用多种不同的编程环境为多种不同的体系结构进行编译。这不是一个前所未闻的概念:在同一代码库中跨多个编译器提供可比的编译器特定功能的替代实现。查看大多数大型库的头文件,您通常会发现类似于“ifdef gcc,然后做这个,ifdef clang,然后做那个”这样的内容。因此,您将采用相同的方法。如果您的代码是用gcc编译的,您将用gcc的init_priority属性标记您的类。如果你的代码是用clang编译的,你会做clang做的任何事情,为了相同的特性,等等……是的,但是过了一段时间就会变得一团糟。对于不同的体系结构,我已经有了大量的
#ifdef
s——为编译器添加更多将变得难以控制。我正在使用Tomasz的方法,到目前为止它工作得很好,应该是交叉编译器。我非常喜欢这个方法,因为它不依赖任何特定的编译器工具,尽管它需要很多额外的编程。也许我应该编写一个小的Perl脚本来为我生成它…@Majenko您可以轻松地将它定义为一个宏,并添加新的颜色,例如
DEF_COLOR(name,r,g,b)
,就像制作Pe一样简单
const Color & Snow(void) {
    static Color snow(255,250,250);
    return snow;
}
#define DEF_COLOR(name, r, g, b) \
const Color & name(void) { \
    static Color name(r,g,b); \
    return name; \
}

DEF_COLOR(Snow,       255,250,250)
DEF_COLOR(GhostWhite, 248,248,255)
// ...