具有初始值的类构造

Class construction with initial values

本文关键字:      更新时间:2023-10-16

我是C++的新手,以及课程的整个想法 - 我仍然在读一本书来尝试和学习。我正在读的书说,当我构造一个类时,我可以通过这样做来分配默认值:

class foo {
public:
   foo(char c, int i);
private:
   char exampleChar;
   int exampleInt;
};
foo::foo(char c, int i):
exampleChar(c),
exampleInt(i)
{}

这段代码(对我来说(看起来非常混乱,并且不遵循我在其他语言中习惯的规则。我的问题是,做上面和这个(下面,我个人认为看起来更干净(有什么区别?

foo::foo(char c, int i) {
   exampleChar = c;
   exampleInt = i;
}

我正在考虑的事情是:如果大规模完成,是否存在性能/效率问题 - 还是完全相同?

第一种方法,通过执行: exampleChar(c), exampleInt(i)称为初始值设定项列表。

如果以第二种方式执行此操作,则默认构造两个变量,首先构造1然后为它们分配一个值。(输入构造函数的实际主体时,初始值设定项列表尚未初始化的任何内容都是默认构造的。这是浪费时间,因为无论如何您都只是覆盖了这些值。对于像 intchar 这样的小类型,这没什么大不了的,但是当这些成员变量是需要大量周期才能构造的大型类型时,您肯定希望使用初始值设定项列表。

第二种方法不会浪费时间给它们一个默认值,然后覆盖它 - 它会将它们的值直接设置为你给它的值(或者如果成员是一个对象,则调用正确的构造函数(。

您可以理解我们这样做的意思:

class MyClass {
public:
    int _i; // our data
    // default constructor
    MyClass() : _i(0) { cout << "default constructor"; }
    // constructor that takes an int
    MyClass(int i) : _i(i) { cout << "int constructor"; }
    // assignment operator
    void operator=(int i) { _i = i; cout << "assignment operator"; }
};
class OtherClass {
public:
    MyClass c;
    OtherClass() {
        c = 54;
    }
};
OtherClass oc;

你会看到

default constructor
assignment operator

被打印出来。这是两个函数调用,对于其他类来说,可能很昂贵。

如果将 OtherClass 的构造函数更改为

OtherClass() : c(54) {   }

你会看到

int constructor

被打印出来。只有一个电话,而不是两个。这是最有效的方法。

初始值设定项列表也是必须的,当您

  1. 具有没有默认构造函数的类型。必须在初始值设定项列表中调用正确的构造函数。

  2. 有一个要提供一些值的const成员(而不仅仅是永久具有默认值(

  3. 具有引用成员。必须对这些使用初始值设定项列表。

DR:这样做是因为它至少和另一种方式一样快,但永远不会慢,有时甚至快得多。

1 对于像intchar这样的内置类型,它们实际上根本不是构造的;它们只是具有它们以前碰巧拥有的任何内存的值。

不同之处在于,编译器将始终在第一个用户定义的构造函数语句之前初始化所有成员(按声明顺序(。在 charint 的情况下,它们都是基元类型,"初始化"在这里实际上意味着"没有初始化"。但是,如果您有一个成员,其构造函数执行一些实际工作,则此构造函数将预先调用 - 如果您这样做

foo::foo() {
    myComplexMember = MyComplexClass(42);
}
编译器在调用

代码之前已经调用了 MyComplexClass 默认构造函数,这是对资源的浪费(如果无法访问默认 CTOR,则会导致编译器错误(。

通过使用初始化列表,您可以自定义默认初始化,避免白白做事。显然,这是要走的路。

有些事情你可以做,否则你不能做。

  1. 如果其中一个成员没有默认构造函数。这是您在施工时启动成员的唯一方法。(基类也是如此(

  2. 您可以为const成员赋值。

  3. 可以在构造函数开始运行之前确保类的已定义状态。

  4. 如果成员是引用,则需要在初始化列表中对其进行初始化。因为引用是不可变的,并且只能在开始时初始化一次(如 const(

嗯,这是一个典型的常见问题解答问题:http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6

在您的情况下,使用 charint 没有区别。

一般规则:尽可能使用初始化列表,在极少数情况下你可以更喜欢赋值,例如为了提高可读性:

MyClass::MyClass
{
  a = b = c = d = e = f = 0;
}

优于

class MyClass::MyClass : a(0), b(0), c(0), d(0), e(0), f(0) { }

如果成员具有非平凡的构造函数,则在下面的代码中首先调用默认构造函数,然后执行赋值,而在上面的代码中,它们将仅初始化一次。所以是的,可能存在性能问题。

还有一个实际问题:如果它们是 const、引用或没有默认构造函数,则不能使用以下版本。

这两个选项之间有一个微妙但重要的区别。 顶部的内容称为成员初始化列表。 创建对象时,此列表中的成员将初始化为您放入括号中的任何内容。

在构造函数主体中执行赋值时,首先初始化值,然后赋值 我将在下面发布一个简短的示例。

示例 1:成员初始化

class foo 
{
public:
   foo(char c, int i);
private:
   char exampleChar;
   int exampleInt;
   Bar exampleBar;
};
foo::foo(char c, int i):
    exampleChar(c),
    exampleInt(i),
    exampleBar()     //Here, a bar is being default constructed
{
}

示例 2:构造函数赋值

class foo 
{
public:
   foo(char c, int i, Bar b);
private:
   char exampleChar;
   int exampleInt;
   Bar exampleBar;
};
foo::foo(char c, int i, Bar b):
    //exampleChar(c),
    //exampleInt(i),
    //exampleBar()  
{
    exampleChar = c;
    exampleInt = i;
    exampleBar = someOtherBar;  //Here, a bar is being assigned
}

这就是它变得有趣的地方。 请注意,正在分配exampleBar。在后台,实际上首先是默认构造Bar,即使您没有指定。 此外,如果你的Bar比一个简单的结构更复杂,你需要确保实现赋值运算符,以便你以这种方式初始化它。 此外,为了从成员初始化列表中的另一个Bar初始化Bar,您必须实现复制构造函数!

示例 3:复制成员初始化中使用的构造函数

class foo 
{
public:
   foo(char c, int i, Bar b);
private:
   char exampleChar;
   int exampleInt;
   Bar exampleBar;
};
foo::foo(char c, int i, Bar b):
    //exampleChar(c),
    //exampleInt(i),
    exampleBar(b)     //Here, a bar is being constructed using the copy constructor of Bar
{
    exampleChar = c;
    exampleInt = i;
}

我会养成使用初始化列表的习惯。当有人将 char 更改为首先调用默认构造函数的某个对象时,他们不会遇到问题,并且常量值的常量正确性也是如此!

foo::foo(char c, int i):exampleChar(c),exampleInt(i){}

此构造在 C++ 中称为成员初始值设定项列表

它将您的成员exampleChar初始化为值c & exampleInt to i


构造函数中的初始化和赋值有什么区别?
优势是什么?

使用初始值设定项列表初始化成员与在构造函数主体中为其赋值之间存在差异。

当您通过初始值设定项列表初始化字段时,构造函数将被调用一次。

如果使用赋值,

则字段将首先使用默认构造函数初始化,然后使用实际值重新分配(通过赋值运算符(。

如您所见,后者中存在创建和赋值的额外开销,这对于用户定义的类来说可能相当可观。

对于整数数据类型(您使用它(或 POD 类成员,没有实际开销。

相关文章:
  • 没有找到相关文章