int/double/etc 的构造函数语义。是:
int a; // uninitialized
int b = int(); // zero initialized
int c = int(4); // four
是否可以定义具有完全相同行为的类?即,一个同时具有未初始化和已初始化的默认构造函数的构造函数?我相信这是不可能的,目前通过制作一个仅在使用 0 调用时编译的构造函数来解决它,但要确保没有办法完全模仿基本类型。
int/double/etc 的构造函数语义。是:
int a; // uninitialized
int b = int(); // zero initialized
int c = int(4); // four
是否可以定义具有完全相同行为的类?即,一个同时具有未初始化和已初始化的默认构造函数的构造函数?我相信这是不可能的,目前通过制作一个仅在使用 0 调用时编译的构造函数来解决它,但要确保没有办法完全模仿基本类型。
这是不可能的,因为与内置类型相反,您的类构造函数将始终被调用。
如果您真的想做“未初始化”对象(为什么????由于未初始化的变量,您没有足够的错误吗?),您可能不得不使用放置技巧new
。
如果没有定义构造函数:
struct A { int x; };
A a; // default-initialized (unintialized)
A b = A(); // value-initialized
A c = { 4 }; // four
“未初始化的构造”是矛盾的。
构造中缺少初始化仅是由于错误(未能递归构造整个对象)而发生的。
如果 C++ 类有构造函数,则调用它。如果由于某种原因不能调用它,那么这种情况是错误的(例如传递的错误构造参数与任何构造函数都不匹配)。
C++ 类可以包含基本类型的成员,其构造函数可以忽略初始化这些成员。
这可以用于优化:如果您有一个稍后发生的复杂初始化,您可以避免写入这些成员的开销。
那将是这样做的方法,而不是尝试创建一个可以保持未初始化的整个类。
如果你真的想要,模拟它的一种方法是创建一个没有任何构造函数的 POD(普通旧数据结构),然后使用一些额外的技术,以便可以以保证其初始化的方式使用它。例如从它创建一个派生类来增加构造,或者利用奇怪重复的模板模式等。
然后,如果您不想初始化 POD,您仍然可以直接使用它。
将始终调用默认承包商。你可以做很多“技巧”,但这取决于你为什么想要这种行为,以及你希望用户代码看起来像什么。您可以定义一个特殊的构造函数,它不对数据成员做任何事情并调用它。或者,你可以做很多不同的事情......一个选择是......
你也可以玩模板...
struct uninitialized_ctor {};
class Foo
{
public:
int x;
Foo() : x(42)
{
std::cout << "Foo::Foo\n";
}
protected:
Foo(uninitialized_ctor const &)
{
std::cout << "Foo::Foo - uninitialized\n";
}
};
struct UninitializedFoo : public Foo
{
UninitializedFoo()
: Foo(uninitialized_ctor())
{
// Does not call base class ctor...
std::cout << "UninitializedFoo\n";
}
};
int main(int, char *[])
{
Foo foo1;
UninitializedFoo foo2;
Foo * f1 = new Foo();
Foo * f2 = new UninitializedFoo();
std::cout << foo1.x << '\n' << foo2.x << '\n' << f1->x << '\n' << f2->x << '\n';
}
但是——这取决于你的真正目标是什么......以及它如何影响代码的用户......
这是不可能的,无论如何都会调用默认构造函数。
聚合类类型基本上具有您喜欢的行为:
struct Foo { int a; int b; };
Foo x; // x.a, x.b uninitialized
Foo * p = new Foo(); // p->x, p->y value-initialized, i.e. zero
Foo y { 1, 2 }; // brace-initialization does what you think
Foo z { }; // brace-initialization can also "fake" value-init for automatics
对于聚合,默认初始化和值初始化递归地传播给成员。
当然,您也可以制作一个非聚合,使成员未初始化:
struct Bar
{
int a;
int b;
Bar() : a() { } // no b!
Bar(int a_, int b_) : a(a_), b(b_) { }
};
Bar x; // x.a is value-, x.b is default-initialized
也许你可以做这样的事情:
template<typename T>
struct uninitialized {
static_assert(std::is_trivially_destructible<T>::value,"T must have trivial dtor");
alignas(T) char raw[sizeof(T)];
template<typename... Us>
T &initialize(Us &&... us) {
return *(new (raw) T(std::forward<Us>(us)...));
}
T &get() { return reinterpret_cast<T&>(raw); }
};
微不足道的析构函数是必需的,因为否则您必须跟踪对象是否已构造,以便适当地销毁它。
class A {
public:
A() {}
};
A* a = (A*)malloc(sizeof(A));
(孩子们,不要在家里这样做。根本不要这样做!)