【问题标题】:C++ constructor definitionC++ 构造函数定义
【发布时间】:2009-08-13 16:37:29
【问题描述】:

这里所有的构造方法都做同样的事情。我主要使用方法2,但今天第一次看到方法3。在某些地方见过method1,但不知道它们之间的确切区别是什么? 哪一种是定义构造函数的最佳方式,为什么?是否涉及任何性能问题?

  1 class Test
  2 {
  3     private:
  4         int a;
  5         char *b;
  6     public:
  7         Test(){};
  8         
  9         // method 1
 10         Test(int &vara, char *& varb) : a(vara), b(varb){}
 11         
 12         // method 2
 13         Test(int &vara, char *& varb)
 14         {
 15             a = vara;
 16             b = varb;
 17         }   
 18         
 19         //method 3
 20         Test(int &vara, char *& varb)
 21         {
 22             this->a = vara;
 23             this->b = varb;
 24         }   
 25         
 26         ~Test(){}
 27 }; 

我这里使用了简单的字段 int 和 char*,如果我有很多字段或复杂类型如 struct 会发生什么??

谢谢

【问题讨论】:

标签: c++


【解决方案1】:

对于您使用的类型,性能可能没有差异。但是对于非 POD 数据(带有构造函数的类),格式为:

Test(int &vara, char *& varb) : a(vara), b(varb){}

将是最有效的。这是因为无论您是否提供初始化列表,都会初始化非 POD 数据。其他使用赋值的形式,会先点击初始化,然后再点击赋值。

【讨论】:

  • 正如我所说,基本上类型带有构造函数、赋值运算符或析构函数——例如 std::string、std::vector 和您自己的类类型。
  • @seg.server.fault:POD 用于“普通旧数据”。见en.wikipedia.org/wiki/Plain_old_data_structures
  • 最重要的是,有些类型没有默认构造函数,所以你需要使用初始化列表!
【解决方案2】:

在性能方面可能没有太大差异,但在方法 1 中,您正在使用复制构造函数构造 a 和 b。在方法 2 中,您将自动构造 a 和 b,然后使用赋值运算符。对于更复杂的类型,可能比 int 和 char* 慢。

方法3和方法2完全一样。

【讨论】:

    【解决方案3】:

    第一种方式,称为“初始化列表”,是最好的。引用属性需要,标准属性更高效。

    第二种方法将为所有属性调用标准构造函数,然后对每个分配的属性使用赋值运算符。它速度较慢,如果属性类型不支持赋值,可能会完全中断。

    第三种方式与第二种方式完全相同,但不必要地冗长。使用 this-> 前缀属性访问是不好的风格,除非有一个局部变量隐藏属性。


    与问题无关,基本类型无需使用引用。您正在为指针取消引用增加额外的开销,并且由于这些值被分配给非引用无论如何,因此没有意义。

    【讨论】:

    • 那么,如果我将 int a 更改为 int &a,那么使用引用是否被认为是好的?什么时候参考是最好的选择?
    • 嗯?不,他的意思是引用用于两件事:非常量引用用于当您希望能够修改参数的值并将该修改保留回调用者时。常量引用用于将小句柄传递给大对象(而不是复制对象)。句柄与整数差不多大,从句柄获取值对性能的影响很小,因此对整数、字符或其他小的基本类型使用引用是没有意义的。
    【解决方案4】:

    方法 2 和方法 3 完全没有区别。在成员函数(包括构造函数和析构函数)中,成员变量 xthis->x 同义。唯一不同的是,如果在更近的范围内还有另一个名为 x 的变量。例如:

    int Foo::memberFunc() {
      return x; // Returns member variable x. Same as return this->x.
    }
    
    int Foo::memberFunc(int x) {
      return x; // Returns the argument x. Need to say return this->x to return member x
    }
    

    方法 1 通常是初始化成员变量的首选方法,因为它明确表明您正在执行此操作。

    在您的情况下,它也与方法 2 和 3 相同,但在某些情况下并非如此。特别是,如果您的任何成员变量是常量、引用或没有默认构造函数的对象,那么 Method1 是您可以初始化它们的唯一方法。

    方法 1 的另一个小区别是,如果您的任何成员变量是对象,则使用方法 2 或 3,它们将首先使用其默认构造函数构造,然后修改或分配给构造函数代码,而使用方法 1 您首先可以使用默认构造函数以外的构造函数来创建它们。

    【讨论】:

    • 是什么让你认为初始化列表的使用与使用默认构造后赋值是“相同的”?
    • 在问题的简单示例中,它是。在更复杂的情况下,情况并非如此。
    【解决方案5】:

    第一种方法(使用初始化列表)是初始化 C++ 类的成员变量的首选方法。它的优点是它允许您为每个成员选择使用哪个构造函数。第二种方法的问题是,在进入构造函数的主体之前,成员字段的默认限制器已经被调用。当基类没有默认构造函数时,这也是调用基类构造函数的唯一方法。其他两个构造函数是相同的,从风格的角度来看,方法 3 是最不受欢迎的。

    【讨论】:

      【解决方案6】:

      如果你有类似的东西,通常使用方法 3

      Test (int &a, char &b) 
      {
          this->a = a;
          this->b = b;
      }
      

      为了完全限定你设置了类字段a和b,基本上和方法2一样。

      【讨论】:

        【解决方案7】:

        他们不做同样的事情。

        方法 1 用指定的值初始化类成员。

        方法 2 和 3 首先默认初始化成员(这对于非 POD 类型意味着调用默认构造函数),然后 然后 调用赋值运算符为它们分配新值。 p>

        换句话说,如果类包含引用,或者如果其中一个成员没有默认构造函数,则 2 和 3 将无法编译。对于大多数非 POD 类型,它也会更慢。

        换句话说,使用初始化列表(方法 1)。这就是它的用途。从正确性以及性能的角度来看,它更好。

        【讨论】:

          猜你喜欢
          • 2021-06-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多