b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

C ++中各种初始化方法的详细说明

电脑杂谈  发布时间:2020-06-11 16:02:14  来源:网络整理

编译 初始化_碎片化时代,学会高效碎片化学习_七日年化收益率 年化收益率

前言

本文主要向您介绍有关C ++初始化方法的相关内容. 分享它供您参考和学习. 我不会谈论太多. 让我们看一下详细的介绍.

C ++小实验测试: 以下程序中主函数a.a和b.b的输出值是什么?

#include <iostream>
struct foo
{
 foo() = default;
 int a;
};
struct bar
{
 bar();
 int b;
};
bar::bar() = default;
int main()
{
 foo a{};
 bar b{};
 std::cout << a.a << '\t' << b.b;
}

答案是a.a为0,b.b是不确定的值(无论您是gcc编译器,clang编译器还是Microsoft msvc ++编译器). 为什么会这样呢?这是因为C ++中的初始化已开始异常地发展.

接下来,我想探讨为什么会这样. 在我们知道原因之前,我们首先给出一些初始化概念: 默认初始化,值初始化和零初始化.

T global;    //T是我们的自定义类型,首先零初始化,然后默认初始化
void foo()
{
 T i;  //默认初始化
 T j{}; //值初始化(C++11)
 T k = T(); //值初始化
 T l = T{}; //值初始化(C++11)
 T m(); //函数声明
 new T; //默认初始化
 new T(); //值初始化
 new T{}; //值初始化(C++11)
}
struct A
{
 T t;
 A() : t() //t将值初始化
 {
 //构造函数
 }
};
struct B
{
 T t;
 B() : t{} //t将值初始化(C++11)
 {
 //构造函数
 }
};
struct C
{
 T t;
 C()  //t将默认初始化
 {
 //构造函数
 }
};

以上不同的初始化形式有点复杂. 我将简化这些C ++ 11初始化:

看上面的例子. 如果T为int类型,则全局变量和使用值初始化形式的T型变量将被初始化为0(因为int是内置类型,而不是类类型或数组,因此它将为零. ,因为int是算术类型,所以如果执行零初始化,则初始值为0),其他默认初始化为未定义的值.

首先回到示例,我们现在已经具有理解该示例所需的基本知识. 结果不同的根本原因是foo和bar受它们在不同位置的默认构造函数影响.

最初要求foo的构造函数是在最初声明时综合的,而不是由我们提供的,因此它属于编译器综合的默认构造函数. 酒吧的建设者是不同的. 它需要在定义时进行综合,因此它属于我们用户定义的默认构造函数.

编译 初始化_七日年化收益率 年化收益率_碎片化时代,学会高效碎片化学习

前面提到的有关值初始化的规则指出: 如果类型T的默认构造函数不是用户定义的,则在默认初始化之前执行零初始化. 由于foo的默认构造函数不是我们的自定义,它是由编译器合成的,因此,在初始化foo类型的对象的值时,将首先执行零初始化,然后调用默认构造函数,这将导致值的aa的初始化为0,bar的默认构造函数是用户定义的,因此不会被初始化为零,而是直接调用默认构造函数,这会导致bb的值未初始化,因此它是随机的每次都有价值.

此陷阱迫使我们注意: 如果您不希望默认构造函数是用户定义的,则必须在类的内部声明中使用“ = default”,而不是在类的外部定义中使用.

对于类类型,用户提供了带有一些其他“副作用”的自定义默认构造函数. 例如,对于缺少用户提供的自定义默认构造函数的类,无法定义该类的const对象. 示例如下:

class exec
{
 int i;
};
const exec e;  //错误!缺少用户自定义默认构造函数,不允许定义const类对象

通过开头的示例,我们对某些C ++初始化方法有直观的体验. C ++中有6种初始化类型: 零初始化,默认初始化,值初始化,直接初始化,副本初始化和列表初始化.

零初始化与变量的类型和位置有关,例如它是静态的还是聚集类型. 可以初始化的类型为0的对象的值全为0,例如int为0,double为0.0,指针为nullptr;

现在我们已经了解了几种初始化规则,以下是几种初始化方法的使用形式:

1. 默认的初始化是在不使用初始化程序的情况下定义对象,即不执行初始化指令时的行为. 典型:

int i;
vector<int> v;

2. 值初始化是定义对象,该对象需要初始化,但不提供初始值的行为. 典型:

int i{};
new int();
new int{}; //C++11

编译 初始化_七日年化收益率 年化收益率_碎片化时代,学会高效碎片化学习

3. 直接初始化和副本初始化主要与自定义对象的初始化有关. 对于内置类型,两者之间没有区别. 对于自定义对象,直接初始化和副本初始化之间的区别是直接调用构造函数或使用“ =”进行初始化. 典型:

vector<int>  v1(10); //直接初始化,匹配某一构造函数
vector<string> v2(10); //直接初始化,匹配某一构造函数
vector<int>  v3=v1;  //拷贝初始化,使用=进行初始化

对于书中给出的示例:

string dots(10, '.'); //直接初始化
string s(dots);      //直接初始化

的s初始化手册说直接初始化. 看起来像是复制初始化. 实际上,这是直接初始化,因为直接初始化使用参数直接匹配某个构造函数. 复制构造函数和其他构造函数就形成了. 重载,以便只调用复制构造函数.

实际上,C ++语言标准规定,复制初始化应首先调用相应的构造函数以创建临时对象,然后复制构造函数,然后将构造的临时对象复制到要创建的对象. 例如:

string a = "hello";

在上面的代码中,由于“ hello”的类型为const char *,因此将首先调用字符串类的字符串(const char *)构造函数以创建一个临时对象,然后copy构造函数将其复制临时反对但是该标准还规定,为了提高效率,允许编译器跳过创建临时对象的步骤,而直接调用构造函数以构造要创建的对象,从而忽略了对复制构造函数的调用以进行优化,即完全等同于直接初始化,当然,您可以使用-fno-elide-constructors选项禁用优化.

如果我们将字符串类型copy构造函数定义为private或delete编译 初始化,则无法对其进行编译. 尽管可以优化它以省略复制构造函数的调用,但是复制构造函数必须在语法上可访问. ,这就是为什么C ++入门第五版第13章的复制控件的13.1.1节结尾处的第442页最后一段说:

“即使编译器跳过了复制/移动构造函数,在此程序点,复制/移动构造函数也必须存在并且可以访问(例如,它不能是私有的).

复制初始化不仅在使用=定义变量时发生,而且在以下特殊情况下也会发生:

七日年化收益率 年化收益率_编译 初始化_碎片化时代,学会高效碎片化学习

1. 将对象作为实际参数传递给未引用的形式参数;

2. 从函数的返回类型为非引用的对象中返回对象;

3. 用花括号初始化数组中的元素或聚合类中的成员.

实际上还有另一种情况,例如当异常被值抛出或捕获时.

另一个令人困惑的部分是vector v2(10). 在“ C ++ Primer 5th”中,这是值初始化的方法,但请仔细阅读本书. 这里的值初始化是指容器中的string元素,也就是说,v2本身是直接初始化的,而v2中的10个字符串元素是因为没有给出初始值,因此标准库使用值初始化方法来初始化容器中的元素.

组合:

只要使用括号(括号或大括号),但没有给出特定的初始值,它就是值初始化. 可以简单地理解为括号告诉编译器您要初始化该对象.

不使用括号,这意味着默认的初始化可以简单地理解,您不用理会它,并允许编译器使用默认行为. 通常这是不良行为,除非您真正了解自己在做什么.

4. 列表初始化是新的C ++标准提供的一种初始化方法. 它可以用于内置类型或自定义对象. 前者是数组,后者是向量. 典型:

int array[5]={1,2,3,4,5};
vector<int> v={1,2,3,4,5};

本文写在这里. 读者在这里仔细阅读它,似乎已经了解了C ++的各种初始化规则和方法. 以下是一些要检查的示例:

七日年化收益率 年化收益率_碎片化时代,学会高效碎片化学习_编译 初始化

#include <iostream>
using namespace std;
class Init1
{
public:
 int i;
};
class Init2
{
public:
 Init2() = default;
 int i;
};
class Init3
{
public:
 Init3();
 int i;
};
Init3::Init3() = default;
class Init4
{
public:
 Init4();
 int i;
};
Init4::Init4()
{
 //constructor
}
class Init5
{
public:
 Init5(): i{}
 {
 }
 int i;
};
int main(int argc, char const *argv[])
{
 Init1 ia1;
 Init1 ia2{};
 cout << "Init1: " << " "
   << "i1.i: " << ia1.i << "\t"
   << "i2.i: " << ia2.i << "\n";
 Init2 ib1;
 Init2 ib2{};
 cout << "Init2: " << " "
   << "i1.i: " << ib1.i << "\t"
   << "i2.i: " << ib2.i << "\n";
 Init3 ic1;
 Init3 ic2{};
 cout << "Init3: " << " "
   << "i1.i: " << ic1.i << "\t"
   << "i2.i: " << ic2.i << "\n";
 Init4 id1;
 Init4 id2{};
 cout << "Init4: " << " "
   << "i1.i: " << id1.i << "\t"
   << "i2.i: " << id2.i << "\n";
 Init5 ie1;
 Init5 ie2{};
 cout << "Init5: " << " "
   << "i1.i: " << ie1.i << "\t"
   << "i2.i: " << ie2.i << "\n";
 return 0;
}

在上面的代码中,主程序的输出值是多少?我们不要先使用编译器来编译程序,而要根据之前介绍的知识来推断一些程序:

首先,我们需要了解,对于类而言,构造函数用于初始化类对象,无论如何都将初始化类对象. 也就是说,在实例化类对象时,无论构造函数是否实际初始化数据成员,都将调用构造函数. 因此,对于没有定义任何构造函数的自定义类,此类的默认构造函数没有“必需/不需要”,并且将不可避免地进行合成.

由于Init1和Init2具有相似的综合默认构造函数,因此它们的ia1.i和ib1.i值相同且应为随机值,而ia2.i和ib2.i需要使用值进行初始化编译 初始化,因此它们的值都为0.

由于Init3和Init4具有类似的用户定义的默认构造函数,因此它们的ic1.i和id1.i值相同并且应该是随机值,而ic2.i和id2.i则需要通过值进行初始化,也是一个随机值.

由于Init5为它明确提供了默认构造函数并手动初始化了数据成员,因此其ie1.i和ie2.i将被初始化为0.

以上是我们的预测. 结果会是这样吗?不幸的是,事实并非一定如此. 我们哪里错了?我们没有错. 上述程序的结果取决于您使用的操作系统,编译器版本(例如gcc-5.0和gcc-7.0)和发行版(例如gcc和clang). 有些人可能会得到与投机完全相同的结果,而另一些人则可能不会. 例如,在经常因批评不符合C ++标准而受到批评的Microsoft VC ++编译器(VS 2017,调试模式)下,结果是完全一致的(可能是由于Microsoft开始接受开源和Linux,并逐渐严格遵守) GCC的结果也完全符合要求,而广受赞誉的Clang也符合要求. 当然,在Mac和Ubuntu上,同一Clang编译器的结果甚至不一致. 在某些时候,GCC比Clang更人性化. 警告会通知您使用了未初始化的数据成员.

尽管由于操作系统和编译器的原因,上述程序中的某些地方与预期结果有所不同,但也必然有相同的地方. 例如,使用构造函数初始化列表的最后一个类的行为符合预期. . 在合成的默认构造函数之前,还有一个初始化零构造函数的地方,它不可避免地会初始化为0.

至此,我们对C ++的初始化方法和规则有了清晰的了解,即: 由于平台和编译器的差异以及对语言标准的不同程度的遵守,我们一定不能依赖合成的默认构造函数. 这就是C ++ Primer反复强调我们不应该依赖综合默认构造函数的原因. 它还显示C ++ Primer告诉我们有关手动分配动态内存的信息. 对于我们的自定义类类型,为什么它不需要值初始化?的.

C ++语言设计的基本思想是``自由''. 它对某些事物给出了特定要求,并留有余地. 未指定的区域是语言的“暗区”. 我们需要小心避免. 在对象的初始化中,建议的方法是删除默认构造函数,让我们的用户定义自己的构造函数,并对每个成员进行合理的初始化. 如果需要保留默认构造函数,则必须执行我知道的行为.

摘要

以上是本文的全部内容. 希望本文的内容对每个人的学习或工作都有一定的参考价值. 如有任何疑问,可以留言和交流. 感谢您对脚本库的支持.


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-241943-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...