【问题标题】:Calling a constructor to reinitialize variables doesn't seem to work?调用构造函数重新初始化变量似乎不起作用?
【发布时间】:2010-03-27 20:39:31
【问题描述】:

我想运行一个程序的 1,000 次迭代,所以在 main 中设置一个 1000 的计数器。我需要在每次迭代后重新初始化各种变量,并且由于类构造函数已经写出了所有初始化 - 我决定在每次迭代后调用它,每次迭代的结果都存储在 main 中的一个变量中。

但是,当我调用构造函数时,它没有任何效果......我花了一段时间才弄清楚 - 但它没有重新初始化任何东西!

我创建了一个与构造函数一模一样的函数——所以对象会有自己的版本。当我调用它时,它会按照我的预期重新初始化所有内容。

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
 */
}

...有人能解释一下为什么我做错了,或者没有用,或者很傻,或者你有什么?我的意思是——在心理上,我只是想——废话,我可以调用这个构造函数并重新初始化所有这些东西。构造函数(理想情况下)是否仅在创建对象时调用?

【问题讨论】:

标签: c++ constructor


【解决方案1】:

您的行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(); 确实定义了一个Class 类型的对象,称为MyClass,并在没有参数的情况下构造它。它声明了一个名为 MyClass 的函数,它不接受任何参数,并返回一个 Class 类型的对象。大概在你的真实代码中,构造函数有一个或多个参数。

【讨论】:

  • 我使用构造函数,但我很少传递参数,即使我知道我可以。大多数时候我只是觉得我应该在源代码的构造函数中设置它们。为什么要通过 main 传递参数?
  • 在这种情况下,如果您没有使用构造函数参数但也没有使用无参数构造函数的正确语法,我对您的代码编译感到非常惊讶。无论如何,使用构造函数参数的原因是如果您的类的对象不完全相同。看看标准库的例子——你可以指定向量的初始大小和内容等等。
  • 使用 Microsoft Visual Studio C++ 和它的内置编译器...如果我将构造函数声明为 KnightsTour::KnightsTour(例如)然后放入 KnightsTour;在我的头文件中,它抛出错误,说它看起来像一个函数,但没有参数。 Soooo ...我添加了一个空参数列表,即“KnightsTour::KnightsTour()”,然后是“KnightsTour();”在头文件中 - 编译并运行!
【解决方案2】:

读到那一行会发生什么...

Class ();

您实际上是否确实调用了构造函数 - 对于从头开始构造的临时对象,然后由于您没有对它做任何事情而立即销毁它。这很像转换为 Class,它使用构造函数调用创建一个值,只是在这种情况下没有要转换的值,所以使用默认构造函数。

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

如果你想重新初始化成员,调用构造函数不是这样做的方法。将所有初始化代码移到另一个方法中,然后从构造函数中调用它,并在您想要重新初始化时调用。

【讨论】:

  • “我不确定这是否允许” - 允许,但前提是构造函数和析构函数都对程序的可观察行为没有任何影响。所以如果你放一些跟踪来看看它们是否被省略了,那么它们就不能被省略。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    您成为了对 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,那就是自找麻烦;-)
    【解决方案5】:

    有这样的需求,我一般写一个clear()(public)方法。我从构造函数、析构函数调用它。用户代码可以随时调用它。

    class Foo
    {
      public:
    
        Foo() { clear(); }
    
        ~Foo() { clear(); }
    
        void clear(); // (re)initialize the private members
    
      private:
    
         // private members
     };
    

    为了回答这里的问题,每当需要重新初始化类时,都可以调用clear() 方法,就像在初始构造之后一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      • 2020-09-27
      相关资源
      最近更新 更多