C++ 类成员初始化差异

C++ 类成员初始化差异,c++,C++,关于复数的类定义,我看到了两种类型的定义: 定义1 class Complex { private: double re; double im; public: Complex(float r,float i) {re = r; im = i;} ~Complex() {}; }; 定义2 class Complex { private: double re; double im; public

关于复数的类定义,我看到了两种类型的定义:

定义1

class Complex
{
   private:
      double re;
      double im;

   public:
      Complex(float r,float i) {re = r; im = i;}
      ~Complex() {};
};
定义2

class Complex
{
   private:
      double re;
      double im;

   public:
      Complex(double r,double i): re(r), im(i) {}
      ~Complex() {};
 };
第一个定义在我看来还可以,但我不太理解第二个定义,它是如何定义的

 Complex(double r,double i): re(r), im(i) {}

工作?“re()”是什么意思

这是一个初始化列表。它将
re
的值设置为
r
,将
im
的值设置为
i


通常使用初始化列表来查看性能增益,但知道何时不使用它也是很重要的。

< P>首先,在C++中的整个类定义之后应该有分号。否则,您的代码将很容易编译

不管怎样

Complex(double r,double i): re(r), im(i) {}
是一个复杂类的构造函数,它只将r的值放入re,将i的值放入im。这是初始化类值的另一种方法

请注意,这样的初始化列表对于初始化类中的成员类非常有用。下面是一个例子:

class MemberClass
{
    private:
        int mValue;

    public:
        MemberClass(int value): mValue(value) {}
};

class MemberHolder
{
    private:
        MemberClass mMember;

    public:
        MemberHolder(int value): mMember(value) {}
};

初始化列表对于在其他类中使用没有默认构造函数的类很重要。

复杂的
构造函数的第二种形式使用的是不同的(和)初始化类成员

re(…)
意味着成员字段
re
应该用传递的任何参数构造

作为另一个示例,您可以创建基本体,如
double
int
,如下所示:

double d(5.0d);
int i(5);

这应该可以解释括号在列表中的作用。

它被称为初始值设定项列表。在类的构造函数中,可以使用此语法初始化成员变量。所以在这里,它相当于把语句
re=r;im=i

对于POD变量,如
int
double
和指针类型,初始值设定项列表语法和正文中的常规赋值之间没有区别。但是,对于
const
变量、引用和具有非平凡构造函数的对象,有一个重要的区别:

  • 对于
    const
    变量和引用变量,它们必须在初始值设定项列表中初始化。不能通过在正文中为它们赋值来初始化它们
  • 对于具有非平凡构造函数的对象,在初始值设定项列表中初始化它们的方式与调用的构造函数相对应。如果省略变量,则调用该对象的默认构造函数(如果该对象没有默认构造函数,则这是编译器错误)
因此,通常建议在初始值设定项列表中初始化具有构造函数的对象,以避免重复工作——如果您通过从初始值设定项列表中忽略它来运行其默认构造函数,然后在构造函数体中执行某种初始化,那么您将对其进行两次初始化,这可能是浪费

例如:

class Example
{
private:
    std::string m_string;
public:
    Example()
    {
        // m_string is first initialized by the std::string default constructor,
        // then we assign to it with operator=(const char *).
        // This is inefficient.
        m_string = "test";
    }

    Example(int dummy)
        : m_string("test")
    {
        // Better: we just call the std::string(const char*) constructor directly
    }
};

在C++中,赋值和初始化之间有区别。
a = 5;  // assignment
int b = 6; // initialization
int b(6);  // also initialization
类的第一个版本在构造函数体中执行赋值。这会更加昂贵,因为数据成员
re
im
首先是默认构造的,然后分配它们的值

在第二个版本中,使用构造函数初始化列表。在这里,使用提供的值初始化数据成员。这发生在一个步骤中,而默认构造函数+赋值是两个步骤。这样更有效

通常,您应该更喜欢在初始化列表中初始化数据成员,而不是在构造函数体中分配它们的值。不过有一个警告。初始化列表中的数据成员按照它们在类中声明的顺序初始化,而不是按照它们在初始化列表中出现的顺序初始化。通常,您希望列表中成员的顺序与其声明的顺序相匹配。否则,如果一个数据成员的初始化取决于另一个数据成员的值,那么您可能最终会遇到很难找到的bug