为什么在 C++ 中显式调用构造函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/827552/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:29:13  来源:igfitidea点击:

Why explicitly call a constructor in C++

c++constructor

提问by Arnkrishn

I know we can explicitly call the constructor of a class in C++ using scope resolution operator, i.e. className::className(). I was wondering where exactly would I need to make such a call.

我知道我们可以使用作用域解析运算符在 C++ 中显式调用类的构造函数,即className::className(). 我想知道我到底需要在哪里打这样的电话。

采纳答案by Klaim

Most often, in a child class constructor that require some parameters :

大多数情况下,在需要一些参数的子类构造函数中:

class BaseClass
{
public:
    BaseClass( const std::string& name ) : m_name( name ) { }

    const std::string& getName() const { return m_name; }

private:

    const std::string m_name;

//...

};


class DerivedClass : public BaseClass
{
public:

    DerivedClass( const std::string& name ) : BaseClass( name ) { }

// ...
};

class TestClass : 
{
public:
    TestClass( int testValue ); //...
};

class UniqueTestClass 
     : public BaseClass
     , public TestClass
{
public:
    UniqueTestClass() 
       : BaseClass( "UniqueTest" ) 
       , TestClass( 42 )
    { }

// ...
};

... for example.

... 例如。

Other than that, I don't see the utility. I only did call the constructor in other code when I was too young to know what I was really doing...

除此之外,我没有看到该实用程序。当我还太小,不知道我真正在做什么时,我才在其他代码中调用了构造函数......

回答by Charlie

You also sometimes explicitly use a constructor to build a temporary. For example, if you have some class with a constructor:

您有时还显式地使用构造函数来构建临时对象。例如,如果您有一些带有构造函数的类:

class Foo
{
    Foo(char* c, int i);
};

and a function

和一个函数

void Bar(Foo foo);

but you don't have a Foo around, you could do

但你身边没有 Foo,你可以这样做

Bar(Foo("hello", 5));

This is like a cast. Indeed, if you have a constructor that takes only one parameter, the C++ compiler will use that constructor to perform implicit casts.

这就像演员表。事实上,如果您有一个只接受一个参数的构造函数,C++ 编译器将使用该构造函数来执行隐式转换。

It is notlegal to call a constructor on an already-existing object. That is, you cannot do

在已经存在的对象上调用构造函数是合法的。也就是说,你不能做

Foo foo;
foo.Foo();  // compile error!

no matter what you do. But you can invoke a constructor without allocating memory - that's what placement newis for.

无论你做什么。但是你可以在不分配内存的情况下调用构造函数——这就是placement new的用途。

char buffer[sizeof(Foo)];      // a bit of memory
Foo* foo = new(buffer) Foo();  // construct a Foo inside buffer

You give new some memory, and it constructs the object in that spot instead of allocating new memory. This usage is considered evil, and is rare in most types of code, but common in embedded and data structure code.

你给 new 一些内存,它在那个位置构造对象而不是分配新的内存。这种用法被认为是邪恶的,在大多数类型的代码中很少见,但在嵌入式和数据结构代码中很常见。

For example, std::vector::push_backuses this technique to invoke the copy constructor. That way, it only needs to do one copy, instead of creating an empty object and using the assignment operator.

例如,std::vector::push_back使用此技术调用复制构造函数。这样,它只需要做一个副本,而不是创建一个空对象并使用赋值运算符。

回答by Alan McBee - MSFT

I think the error message for compiler error C2585 gives the best reason why you would need to actually use the scope-resolution operator on the constructor, and it does in with Charlie's answer:

我认为编译器错误 C2585 的错误消息给出了您需要在构造函数上实际使用范围解析运算符的最佳理由,它与查理的回答有关:

Converting from a class or structure type based on multiple inheritance. If the type inherits the same base class more than once, the conversion function or operator must use scope resolution (::) to specify which of the inherited classes to use in the conversion.

从基于多重继承的类或结构类型转换。如果该类型多次继承同一个基类,则转换函数或运算符必须使用作用域解析 (::) 来指定在转换中使用哪个继承的类。

So imagine you have BaseClass, and BaseClassA and BaseClassB both inherit BaseClass, and then DerivedClass inherits both BaseClassA and BaseClassB.

所以假设你有 BaseClass,BaseClassA 和 BaseClassB 都继承了 BaseClass,然后 DerivedClass 继承了 BaseClassA 和 BaseClassB。

If you are doing a conversion or operator overload to convert DerivedClass to a BaseClassA or BaseClassB, you will need to identify which constructor (I'm thinking something like a copy constructor, IIRC) to use in the conversion.

如果您正在执行转换或运算符重载以将 DerivedClass 转换为 BaseClassA 或 BaseClassB,您将需要确定在转换中使用哪个构造函数(我正在考虑类似于复制构造函数,IIRC)。

回答by jmucchiello

In general you do not call the constructor directly. The new operator calls it for you or a subclass calls the parent class' constructors. In C++, the base class is guarenteed to be fully constructed before the derived class' constructor starts.

通常,您不会直接调用构造函数。new 运算符为您调用它,或者子类调用父类的构造函数。在 C++ 中,保证在派生类的构造函数开始之前完全构造基类。

The only time you would call a constructor directly is in the extremely rare case where you are managing memory without using new. And even then, you shouldn't do it. Instead you should use the placement form of operator new.

直接调用构造函数的唯一时间是在极少数情况下,您在不使用 new 的情况下管理内存。即使那样,你也不应该这样做。相反,您应该使用 operator new 的放置形式。

回答by jls

There are valid use cases where you want to expose a classes constructors. If you wish to do your own memory management with an arena allocator for example, you'll need a two phase construction consisting of allocation and object initialization.

有一些有效的用例,您希望公开类构造函数。例如,如果您希望使用 arena 分配器进行自己的内存管理,则需要一个由分配和对象初始化组成的两阶段构造。

The approach I take is similar to that of many other languages. I simply put my construction code in well known public methods (Construct(), init(), something like that) and call them directly when needed.

我采用的方法类似于许多其他语言的方法。我只是将我的构造代码放在众所周知的公共方法(Construct()init(),类似的东西)中,并在需要时直接调用它们。

You can create overloads of these methods that match your constructors; your regular constructors just call into them. Put big comments in the code to warn others that you are doing this so they don't add important construction code in the wrong place.

您可以创建与您的构造函数匹配的这些方法的重载;您的常规构造函数只是调用它们。在代码中添加大注释以警告其他人您正在这样做,这样他们就不会在错误的位置添加重要的构造代码。

Remember that there is only one destructor method no matter which construction overload was used, so make your destructors robust to uninitialized members.

请记住,无论使用哪种构造重载,都只有一种析构函数方法,因此要使析构函数对未初始化的成员具有健壮性。

I recommend against trying to write initializers that can re-initialize. It's hard to tell the case where you are looking at an object that just has garbage in it because of uninitialized memory vs. actually holding real data.

我建议不要尝试编写可以重新初始化的初始化程序。很难判断您正在查看一个因为未初始化的内存而不是实际保存真实数据而只包含垃圾的对象的情况。

The most difficult issue comes with classes that have virtual methods. In this case the compiler normally plugs in the vtable function table pointer as a hidden field at the start of the class. You can manually initialize this pointer, but you are basically depending on compiler specific behavior and it's likely to get your colleagues looking at you funny.

最困难的问题来自具有虚方法的类。在这种情况下,编译器通常会在类的开头插入 vtable 函数表指针作为隐藏字段。您可以手动初始化此指针,但您基本上取决于编译器的特定行为,并且很可能让您的同事看着您很有趣。

Placement new is broken in many respects; in the construction/destruction of arrays is one case so I tend not to use it.

Placement new 在很多方面都被打破了;在数组的构造/销毁中是一种情况,所以我倾向于不使用它。

回答by Devesh Agrawal

Consider the following program.

考虑以下程序。

template<class T>
double GetAverage(T tArray[], int nElements)
{
T tSum = T(); // tSum = 0

for (int nIndex = 0; nIndex < nElements; ++nIndex)
{
    tSum += tArray[nIndex];
}

// Whatever type of T is, convert to double
return double(tSum) / nElements;
}

This will call a default constructor explicitly to initialize the variable.

这将显式调用默认构造函数来初始化变量。

回答by Jonathan Schuster

I don't think you would typically use that for the constructor, at least not in the way you're describing. You would, however, need it if you have two classes in different namespaces. For example, to specify the difference between these two made-up classes, Xml::Elementand Chemistry::Element.

我认为您通常不会将它用于构造函数,至少不会以您描述的方式使用它。但是,如果您在不同的命名空间中有两个类,您将需要它。例如,要指定这两个组合类之间的区别,Xml::ElementChemistry::Element.

Usually, the name of the class is used with the scope resolution operator to call a function on an inherited class's parent. So, if you have a class Dog that inherits from Animal, and both of those classes define the function Eat() differently, there might be a case when you want to use the Animal version of eat on a Dog object called "someDog". My C++ syntax is a little rusty, but I think in that case you would say someDog.Animal::Eat().

通常,类的名称与作用域解析运算符一起使用以调用继承类的父类上的函数。因此,如果您有一个继承自 Animal 的类 Dog,并且这两个类对函数 Eat() 的定义不同,那么可能会出现您想要在名为“someDog”的 Dog 对象上使用 Eat 的 Animal 版本的情况。我的 C++ 语法有点生疏,但我认为在这种情况下你会说someDog.Animal::Eat().