调用构造函数重新初始化变量似乎无效?

7
我想运行1000次程序,所以在主函数中设置了一个计数器为1000。在每次迭代之后,我需要重新初始化各种变量。由于类构造函数已经编写好了所有的初始化内容,因此我决定在每次迭代后调用它,并将每次迭代的结果存储在主函数的一个变量中。
然而,当我调用构造函数时,它没有任何效果……我花了一些时间才弄清楚——它没有重新初始化任何东西!
我创建了一个与构造函数完全相同的函数,使对象拥有自己的版本。当我调用它时,它按照我预期的方式重新初始化了所有内容。
int main()
{
 Class MyClass()

 int counter = 0;

 while ( counter < 1000 )
 { stuff happens }

 Class(); // This is how I tried to call the constructor initially.
          // After doing some reading here, I tried:
          // Class::Class(); 
          // - but that didn't work either 
 /* Later I used...
 MyClass.function_like_my_constructor; // this worked perfectly
 */
}

有人能否尝试解释一下我做错了什么,或者为什么不起作用,或者是愚蠢的,或者其他什么原因?我的意思是,从心理上讲,我认为 - 哎呀,我可以调用这个构造函数,重新初始化所有这些东西。构造函数(理想情况下)只有在对象创建时才被调用吗?


似乎可以使用placement new实现。https://dev59.com/gFnUa4cB1Zd3GeqPdLnX - eonil
MyClass = Class(); ? - Hello World
5个回答

9

您的代码行 Class(); 调用了类 Class 的构造函数,但这是为了创建一个“临时对象”。由于您没有使用该临时对象,因此该行代码没有任何有用的效果。

临时对象(通常)会在它们出现的表达式结束时消失。它们对于作为函数参数传递或初始化其他对象非常有用。仅在语句中创建一个临时对象几乎从不有用。尽管语言允许它作为有效的表达式,但对于大多数类而言,它并没有做太多事情。

C++ 中没有办法调用已经构造的对象的构造函数。C++ 对象的生命周期只有一次构造和一次销毁。这就是它的工作原理。如果您想在对象的生命周期内重置它,您已经采取了正确的方法,即调用一个函数来重置它。根据您的类,您可能不需要编写一个 - 默认赋值运算符可能正好做你所需的一切。这就是临时对象变得有用的时候:

Class myObject;
// ... do some stuff to myObject ...

myObject = Class();

这将使用新构造的临时对象的值更新myObject。这并不一定是最有效的代码,因为它创建了一个临时对象,然后复制,然后销毁临时对象,而不是仅将字段设置为其初始值。但除非你的类非常庞大,否则进行1000次所有操作不太可能花费明显的时间。

另一个选项是为每个迭代使用全新的对象:

int main() {
    int counter = 0;
    while (counter < 1000) {
        Class myObject;
        // stuff happens, each iteration has a brand new object
    }
}

请注意,Class MyClass();不是定义一个名为MyClass的Class类型对象,并且不带任何参数构造它。它声明了一个名为MyClass的函数,该函数不带参数并返回一个Class类型的对象。在您的实际代码中,构造函数可能有一个或多个参数。

我使用构造函数,但很少传递参数,尽管我知道我可以这样做。大部分时间里,我只是觉得应该在源代码中的构造函数里进行设置。为什么要通过主函数来传递参数呢? - Azoreo
在这种情况下,如果你没有使用构造函数参数,但也没有使用无参数构造函数的正确语法,那么我非常惊讶你的代码能够编译通过。无论如何,使用构造函数参数的原因是如果你的类对象不都是相同的。查看标准库的示例 - 你可以指定向量的初始大小和内容等。 - Steve Jessop
使用Microsoft Visual Studio C++及其内置编译器...如果我将构造函数声明为KnightsTour::KnightsTour(例如),然后在我的头文件中放置KnightsTour;,它会抛出错误,说它看起来像一个函数,但没有参数。所以...我添加了一个空参数列表,即“KnightsTour :: KnightsTour()”,然后在头文件中添加“KnightsTour();” - 编译并运行! - Azoreo

5

在那行代码中发生了什么...

Class ();

实际上,您正在调用构造函数 - 用于从头开始构造的临时对象,并立即将其销毁,因为您没有对其执行任何操作。这非常类似于转换为Class,它使用构造函数调用创建值,只是在这种情况下没有值可转换,因此使用默认构造函数。

编译器可能会优化掉这个临时对象,因此根本没有构造函数 - 我不确定是否允许。

如果要重新初始化成员,请不要使用调用构造函数的方式。将所有初始化代码移动到另一个方法中,并从构造函数中调用该方法,以便在需要重新初始化时进行调用。


2
“我不确定是否允许这样做” - 如果构造函数或析构函数对程序的可观察行为没有任何影响,那么是可以的。因此,如果您添加了一些跟踪来查看它们是否被省略,则不能省略它们。 - Steve Jessop

1

是的,这不是典型的用法。创建一个重置变量的函数,并在需要的时候调用该方法。


0

你陷入了一个常见的c++误读中。新的c++0x使事情变得更加清晰。

问题在于构造语法看起来像函数调用。

void foo( int i ) { }
class Foo { };

Foo(10); // construct a temporary object of type foo
foo(10); // call function foo
Foo{10}; // construct a temporary object of type foo in c++0x syntax

我认为c++0x语法更加清晰。

你可以使用这种语法来实现你想要的功能。但是要注意,它非常高级,你不应该这样做。

MyClass.~Class(); // destruct MyClass
new( &MyClass ) Class;

不这样做的主要实际原因是,如果Class的构造函数抛出异常,则对象MyClass处于不一致状态。堆栈展开将尝试再次销毁它,导致未定义的行为。此外,与良好编写的operator=reset成员函数相比,它并没有带来任何好处。因此,如果您控制MyClass,则没有必要这样做;如果您不控制MyClass,那么这是在寻找麻烦;-) - Steve Jessop

0
根据这样的要求,我通常会编写一个名为clear()的公共方法。我在构造函数和析构函数中调用它。用户代码可以随时调用它。
class Foo
{
  public:

    Foo() { clear(); }

    ~Foo() { clear(); }

    void clear(); // (re)initialize the private members

  private:

     // private members
 };

回答这个问题,clear()方法可以在需要重新初始化类的情况下调用,使其恢复到初始构造后的状态。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接