构造函数除了向成员传递值之外,还能做任何困难的工作吗

Can constructor do any hard job other than passing values to members?

本文关键字:任何困 工作 成员 构造函数      更新时间:2023-10-16

因此,为了总结C++中的初始化,构造函数可以

  1. 将基元类型初始化为内存中的垃圾值
  2. 自动调用成员上的默认构造函数
  3. 使用init列表中的非平凡构造函数初始化成员对象

但它无法正确初始化数组。参考编号:C++:数组的构造函数初始值设定项

我的问题是:如何在函数体中进行有意义的初始化?在输入大括号之前,所有内容都已经以某种方式进行了初始化。牙套只包含清洁工作,比如cout<lt;"这个对象是构造的!"。参考:构造函数在C++中的应用

如果我想在将参数传递给成员对象的构造函数之前对其进行预处理,该怎么办?例如:

class Foo{
public:
    int x;
    Foo(int y){
        x = y;
    }
};
class Bar{
public:
    Foo foo1;
    Bar(int y){
        int z = superComplicatedOperation(y);
        // and all other heavylifting work
        foo1 = A(z);
    }
};

上面的代码没有编译,因为编译器试图在构造函数中输入大括号之前初始化foo1。

我看到了通过为Foo提供默认构造函数来解决这个问题的建议。因此,Bar的构造函数将首先将foo1初始化为垃圾值,然后将foo1分配给ctor主体中有意义的东西。这也是引用线程中数组初始化的解决方案。但"初始化为垃圾值"不是一种矛盾修辞法吗?如果编译器希望我这样做,那么它不会抱怨没有默认的ctor。

总之,ctor应该以用户定义的方式初始化对象,但我看不出有任何方法可以进行非平凡的初始化。我是不是遗漏了什么?

=============================================

澄清:我问我很清楚Bar(int y(:foo(superComplicatedOperation(y((语法。我的问题是如何做好这件事。从设计的角度来看,我认为superComplicatedOperation((中的代码确实应该属于actor。例如,假设Bar使用2个参数(int x,int y(进行初始化。我们可以想象它们是平面上的xy坐标。Foo也获取坐标,但使用不同的参考系。那我需要

Bar(int x,int y):foo(f1(a,b),f2(a,b))

如果ctor有几个param,这种事情很快就会变得丑陋起来。此外,init列表看起来很拥挤。

如果ctor-body"接触"了成员对象,这意味着init-list并没有将成员设置为所需的状态,而是一些默认的类零状态(例如空向量(。因此,当调用ctor时,首先分配Bar的内存,然后成员对象(内存的子簇(经过以下阶段:

垃圾值/未初始化
---init列表或默认成员ctor-->默认的"类零"状态
---ctor主体------------------------>实际期望的初始状态

那么初始化应该是一个两步的过程?

预处理参数相当简单:

class Bar
{
    Foo foo;
    Bar(int y) : foo(superComplicatedOperation(y)) {}
}

或者您可以使用C++11委托构造函数:

class Bar
{
    Foo foo;
    Bar (SuperComplicatedData z) : foo(z) {}
    Bar (int y) : Bar(superComplicatedOperation(y)) {}
}

在构造函数函数体中可以做什么?很多事情:填充容器、配置成员对象、设置成员对象关系等。

[…]构造函数可以

  1. 将基元类型初始化为内存中的垃圾值
  2. 自动调用成员上的默认构造函数

这是默认初始化,适用于未在初始值设定项列表中指定并且在类定义中没有初始值设定值的每个成员。

  1. 使用init列表中的非平凡构造函数初始化成员对象

成员初始值设定项列表也可以初始化基元成员。

但它无法正确初始化数组。参考编号:C++:数组的构造函数初始值设定项

其中一个答案指出,这现在可以在C++11中完成。

我的问题是:如何在函数中进行有意义的初始化身体在进入大括号之前,一切都已经以某种方式初始化。支架只包含清洁工作,像cout<lt;"这个对象是构造的!"。参考编号:构造函数在C++中的应用

嗯。。。不。构造函数本体中经常有实质性的逻辑。这绝对不仅仅是为了"清理"。的确,在您输入构造函数主体时,每个成员都以某种方式初始化(可能包括基元类型的"根本没有初始化"(,但没有任何内容表明您不能分配或以其他方式修改它们。有时,如果存在某种循环依赖关系,则必须这样做。有时,计算存储值的代码足够长,因此在构造函数体中可读性要高得多。

上面的代码没有编译,因为编译器试图初始化foo1在构造函数中输入大括号之前。

我看到了通过提供默认值来解决此问题的建议构造函数到Foo。

好吧,你可以在你的例子中做: foo1(A(superComplicatedOperation(y))

因此,Bar的构造函数将首先初始化foo1到垃圾值,然后将foo1分配给ctor主体。这也是在引用的线程。但"初始化为垃圾值"不是一种矛盾修辞法吗?如果编译器希望我这样做,那么它不会抱怨没有默认的ctor。

默认的ctor表示用它构造的对象是有效的对象。默认的ctor不一定会使成员未初始化(或"初始化为垃圾值"(。考虑一下std::vector的默认ctor,它最好将其内部状态设置为表示空向量的值!

更根本的是,构造函数的工作(无论是否默认(是建立对象的不变量,对对象进行操作的函数依赖于该不变量。有些对象可能没有不变量,但许多对象有(例如,vector的内部指针最好是有效指针或null(。如果没有默认的构造函数,那么当没有提供参数时,编译器就无法创建有效的对象;它不能只给你一些"初始化为垃圾值"的东西,因为它无法知道它是否真的是那种类型的有效对象。

您可以使用初始值设定项列表,对于任何需要初始化的成员变量都应该使用该列表。

您可能更喜欢将事物初始化为正常值,然后使用一个稍后可以设置值的函数。它并不那么干净,但如果您计划构造多个对象,并且可以同时预计算其中多个对象的值,则可能会节省一些时间,而不是计算每个对象的值。

#include <iostream>
int superComplicatedOperation(int a)
{
    return a * 10;
}
class Foo
{
    public:
    int x;
    Foo(int y)
        : x(y)
    {
    }
};
class Bar
{
    public:
    Foo foo1;
    Bar(int y)
        : foo1(superComplicatedOperation(y))
    {
    }
};
int main()
{
    Bar b(7);
    std::cout << b.foo1.x << "n";
    return 0;
}
class Bar {
public:
    Foo foo1;
    Bar(int y) : foo1(superComplicatedOperation(y)) { }
};

您可以使用构造函数的初始化列表来完成这项工作。它在第一次创建时将值传递给成员的Ctor,这样可以防止创建垃圾对象。

我想我不明白。我在20世纪90年代写过代码,它有一个包含数百行代码的构造函数,可以做各种事情:打开文件,实例化几十个对象,这些对象基于处理文件数据创建链表和数组。它当然可以正确地初始化数组。

相关文章: