使用默认构造函数强制未初始化的成员声明 [英] Forcing uninitialised declaration of member with a default constructor

查看:96
本文介绍了使用默认构造函数强制未初始化的成员声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我今天发现了这种现象,其中不必要地构造了一个成员两次:

I discovered this phenomenon today, where a member is unnecessarily constructed twice:

#include <iostream>

class Member {
public:
    Member() {
        std::cout << "Created member (default)" << std::endl;
    }

    Member(int i) {
        std::cout << "Created member: " << i << std::endl;
    }
};

class Object {
    Member member;

public:
    Object() {
        member = 1;
    }
};

int main() {
    Object o;
    return 0;
}

有没有一种方法可以声明成员 未初始化 -而不是使用默认的构造函数-从而迫使您在构造函数中使用初始化程序列表?

Is there a way to declare the member uninitialised - instead of using the default constructor - hence forcing you to use the initialiser lists in the constructor?

在Java中,如果您这样定义成员:成员i; ,并且您没有在每个构造函数中都对其进行初始化,当您尝试使用该字段时,会收到一条错误消息,指出该字段可能未初始化。

In Java, if you define a member like so: Member i; and you don't initialise it in every constructor, you'll get an error saying the field may be uninitialised, when trying to use it.

如果我从 Member 类中删除默认构造函数,则会得到我想要的行为-编译器强制您为每个构造函数使用初始化程序列表-但我想要这个一般情况下,要使我避免忘记使用此表单(当有默认构造函数时)。

If I remove the default constructor from the Member class, I get the behaviour I want - the compiler forces you to use an initialiser list for every constructor - but I want this to happen in general, to stop me from forgetting to use this form instead (when a default constructor is available).

本质上,我想防止错误地使用使用默认构造函数,但看起来不存在...

Essentially, I wanted protection against mistakenly using the default constructor, but it looks like this doesn't exist...

即使在使用 explicit 关键字标记构造函数时,成员m ember 仍会生成一个成员-在构造函数中重新分配该成员后会立即将其丢弃。这本身似乎也不一致...

Even when marking the constructor with the explicit keyword, Member member still generates a member - that's immediately discarded when it's reassigned in the constructor. This itself seems inconsistent as well...

我的主要问题是不一致。如果没有默认构造函数,则可以声明一个未初始化的成员。这实际上是有用的;您无需提供初始的冗余声明,只需在构造函数中进行初始化(如果未初始化则中断)。具有默认构造函数的类完全没有此功能。

My main problem is the inconsistency. You can declare an uninitialised member if it has no default constructor; this is actually useful; you don't need to feed an initial redundant declaration, but simply initialise at the constructor (and break if not initialised). This functionality is completely missing for classes with a default constructor.

一个相关的示例是:

std::string s;
s = "foo"; 

您可以简单地执行以下操作: std :: string s =" foo" ;; ,但是,如果 foo 实际上是多行-与单个表达式相反-我们得到非原子初始化。

You could simply do: std::string s = "foo"; instead, however if "foo" is actually multiple lines - as opposed to a single expression - we get non-atomic initialisation.

std::string s = "";
for (int i = 0; i < 10; i++) s += i;

这种初始化很容易以写入撕裂的方式结束。

This initialisation could easily end up in a torn write.

如果您拆分像这样,它几乎是原子分配的,但是您仍然将默认值用作占位符:

If you split it up, like so, it's assigned nearly atomically, however you still have the default value used as a placeholder:

std::string member;
// ...
std::string s = "";
for (int i = 0; i < 10; i++) s += i;
member = s; 

在此代码中,您实际上可以简单地移动成员变量 s 完全构建后下降;但是,在类中,这是不可能的,因为具有默认构造函数的成员必须在初始化时初始化-尽管没有默认构造函数的成员也没有以相同的方式受到限制。

In this code, you could actually simply move the member variable down after s is fully constructed; however, in a class, this isn't possible, as a member with a default constructor must be initialised at decleration - despite members without a default constructor not being restricted in the same way.

在上述情况下, std :: string 的默认构造函数的冗余使用相对便宜,但并不能在所有情况下都适用

In the above case, the redundant use of std::string's default constructor is relatively inexpensive, but that wouldn't hold for all situations.

我不希望默认构造函数消失,我只希望有一个选项可以使成员保持未初始化状态,直到构造函数出现为止-与我一样没有默认构造函数的类型。在我看来,这似乎是一个简单的功能,我为为什么不支持它感到困惑//

I don't want the default constructor gone, I just want an option to leave the member uninitialised until the constructor - the same way I can with types with no default constructor. To me, it seems like such a simple feature and I'm puzzled by why it's not supported/

似乎这是自然的实现(无论何时没有默认构造函数的类型的未初始化类型声明),如果不是,则用于支持的类的无括号实例化,该类将默认实例化类-即使您希望它们未初始化,例如我的情况。

It seems that this would have naturally been implemented (whenever uninitialised declaration of types with no default constructor was) if not for bracketless instantiation of a class being supported, which presumptuously instantiates classes - even when you want them left uninitialised, like my situation.

编辑:再次遇到此问题

在Java中,您可以这样做

In java you can do this

int x; // UNINITIALISED
if (condition){
   x = 1; // init x;
}
else return;
use(x); // INITIALISED

在c ++中这是不可能的???
使用默认构造函数初始化,但这不是必需的-这很浪费。
-注意:您不能使用未初始化的变量
如您所见,由于我在循环外使用 x ,因此必须在该位置声明它,这时-不必要地进行了初始化。
另一种情况是有用的,其中 int x = delete 。它不会破坏任何代码,并且仅在尝试使用未初始化的x时导致编译时错误。
没有未初始化的内存或不确定状态,这只是编译时的事情-Java能够很好地实现。

In c++ this is not possible??? It initialises with the default constructor, but this isn't necessary - its wasteful. - note: you can not use the uninitialised variable. As you can see, because I'm using x outside of the loop, it has to get declared there, at which point it's - unnecessarily - initialised. Another scenario where int x = delete would be useful. It would break no code, and only cause a compile-time error when trying to use the uninitialised x. There's no uninitialised memory or undeterministic state, it's simply a compile-time thing - that Java has been able to implement well.

推荐答案

请务必记住,C ++不是Java。在C ++中,变量是对象,而不是对对象的引用。在C ++中创建对象时,您已经创建了对象。调用默认构造函数以创建对象与调用任何其他构造函数一样有效。在C ++中,一旦输入了类的构造函数的主体,则其所有成员子对象都是完整的对象(至少就语言而言)。

It's important to remember that C++ is not Java. In C++, variables are objects, not references to objects. When you create an object in C++, you have created an object. Calling a default constructor to create an object is just as valid as calling any other constructor. In C++, once you enter the body of a class's constructor, all of its member subobjects are fully-formed objects (at least, as far as the language is concerned).

如果某个类型具有默认构造函数,则意味着使用该默认构造函数创建该类型的实例100%可以。这样的对象不是未初始化的。

If there is some type which has a default constructor, that means that it is 100% OK for you to use that default constructor to create an instance of that type. Such an object is not "uninitialized"; it is initialized via its default constructor.

简而言之,您认为默认的构造对象未初始化是错误的,否则它是无效的。 。除非默认构造函数显式地使对象处于非功能状态,否则不会这么做。

In short, it is wrong for you to consider a default constructed object "uninitialized" or otherwise invalid. Not unless that default constructor explicitly leaves the object in a non-functional state.


我不希望默认构造函数消失,我只是希望有一个选项可以让成员保持未初始化的状态,直到构造函数为止-与没有默认构造函数的类型一样。

I don't want the default constructor gone, I just want an option to leave the member uninitialised until the constructor - the same way I can with types with no default constructor.

同样,C ++不是Java。 C ++中的未初始化一词意味着与您处理Java时完全不同。

Again, C++ is not Java. The term "uninitialized" in C++ means something completely different than when you're dealing with Java.

Java声明引用,C ++声明 objects (和参考,但必须立即绑定)。如果对象是未初始化的,则在C ++中它仍然是对象。该对象具有未定义的值,因此访问方式受到限制。但是就C ++的对象模型而言,它仍然是一个完整的整体对象。

Java declares references, C++ declares objects (and references, but they have to be bound immediately). If an object is "uninitialized", it is still an object in C++. The object has undefined values, and thus you are limited in how you may access it. But it is still a complete and total object as far as C++'s object model is concerned. You can't construct it later (not without placement-new).

在Java中,将变量保留为未初始化状态意味着没有对象。这是一个空引用。 C ++没有等效的语言概念,除非存在问题的成员是指向对象的指针,而不是对象本身,否则C ++没有等效的语言概念。

In Java, to leave a variable uninitialized means that there is no object; it's a null reference. C++ has no equivalent language concept, not unless the member in question is a pointer to an object rather than the object itself. Which is a pretty heavy-weight operation.

无论如何,在C ++中,类的作者有权限制该类的工作方式。这包括如何初始化。如果类的作者希望确保总是初始化该对象中的某些值,那么他们就可以这样做,并且没有什么,您可以停止它。

In any case, in C++, the author of a class has the right to restrict how that class works. This includes how it gets initialized. If the author of a class wants to ensure that certain values in that object are always initialized, then they get to do that and there is nothing you can do to stop it.

通常来说,您应该避免尝试做您正在做的事情。但是,如果有某种类型必须在构造函数成员初始值设定项列表之外进行初始化,并且不想调用其默认构造函数(或者没有默认构造函数),则可以使用 std :: optional< T> ,其中 T 是有问题的类型。 可选的含义是:一个可能包含或可能不包含 T 的对象。它的默认构造函数以 T 开始,但是您可以使用可选创建一个新的 T : :emplace 。并且您可以使用-> * T c $ c>。但是它从来不会堆分配 T ,所以您没有那笔开销。

Generally speaking, you should avoid trying to do what you're doing. If however there is some type that you must initialize outside of the constructor member initializer list, and you don't want to call its default constructor (or it doesn't have one), then you can use std::optional<T>, where T is the type in question. optional is what it sounds like: an object that may or may not hold a T. Its default constructor starts without a T, but you can create a new T with optional::emplace. And you can access the T with pointer syntax like -> or *. But it never heap-allocates the T, so you don't have that overhead.

这篇关于使用默认构造函数强制未初始化的成员声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆