我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++变量初始化形式及其默认初始值问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++变量初始化形式及其默认初始值问题

什么是初始化

当对象在创建时获得了一个特定的值,我们就说这个对象被初始化了。

注意:在C++语言中,初始化和赋值是两个完全不同的操作。

初始化:创建变量时赋予其一个初始值。

赋值:把对象的当前值删除,并赋予一个新的值。

而在很多类中,初始化和赋值的区别事关底层效率问题:前者直接初始化数据成员,后者则先初始化再赋值。

初始化方式

默认初始化

在下面情况发生:

在块作用域中定义非静态变量或者数组时没有赋初值

{
	int var;
	int arr[10];
}

当一个类本身含有类类型的成员且使用合成的默认构造函数时

class B {
	int a = 1;
	int b = 2;
};
class A {
	B m_b;
};

当类类型的成员没有在构造函数初始化列表中显示地初始化时

简单来说,如果在变量初始化时没有指定初始值,则变量进行默认初始化,此时变量被赋予了默认值,默认值到底是什么由变量类型变量的位置决定的,我们后面会具体讲解

值初始化

值初始化是只使用了初始化器(即使用了圆括号或花括号)但却没有提供初始值的情况

int main()
{
	int *p = new int();//值初始化
	vector<int> vec(10);//值初始化
	//int a();错误的初始化方式
	int a = int();//值初始化
	return 0;
}

注意:当不采用动态分配内存的方式(即不采用new运算符)时,写成int a();是错误的值初始化方式,因为这种方式声明了一个函数而不是进行值初始化。如果一定要进行值初始化,必须结合拷贝初始化使用,即写成int a=int();

  • 对于内置类型初始值为0
  • 对于类类型则调用其默认构造函数,如果没有默认构造函数,则不能进行值初始化。
class A {//由于显示声明了构造函数,所以没有默认构造函数
public:
	A(int x) {
		a = x;
	}
	int a;
};


int main()
{
	A object{};//由于没有默认构造函数,初始化出错
	cout << object.a << endl;
	return 0;
}

直接初始化/拷贝初始化

直接初始化与拷贝初始化对应,其内部实现机理不同。

  • 直接初始化在下面情况下发生
  • 采用圆括号的方式进行变量初始化
  • 与值初始化不同,括号里一定要有初始值
  • 用emplace成员创建的元素都进行直接初始化

拷贝初始化在下面情况下发生

  • 采用等号(=)进行初始化
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用列表初始化一个数组中的元素
int main()
{
	int a(5);//直接初始化
	vector<int>vec1(10);//值初始化
	vector<int>vec2(vec1);//直接初始化
	vector<int>vec3(10,1);//直接初始化
	int b = 10;//拷贝初始化;
	vector<int>vec4 = vec3;//拷贝初始化
}

当使用直接初始化时,我们实际上是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。

注意:虽然拷贝初始化看起来像是给变量赋值,实际上是执行了初始化操作,与先定义再赋值本质不同。

可以看下面例子:

在这里插入代码片class Foo
{
public:
	Foo() {
		cout << "Foo()" << endl;
	};
	Foo(int n) {
		cout << "Foo(int n)" << endl;
	}
	Foo(const Foo&x) {
		cout << "Foo(const Foo&x)" << endl;
	}
	Foo& operator=(const Foo&x) {
		cout << "Foo& operator=(const Foo&x) " << endl;
		return *this;
	}
	Foo& operator+(const Foo&x) {
		a += x.a;
		cout << "Foo& operator+(const Foo&x) " << endl;
		return *this;
	}
	int a=1;
};
int main()
{
	Foo f1;//默认初始化
	Foo f2 = f1;//拷贝初始化
	Foo f3=f1+f2;//拷贝初始化
	f3 = f2;//赋值操作
}

输出结构:

可以看到在Foo f3=f1+f2;这行代码中并没有执行赋值操作

(1)对于内置类型变量(如int,double,bool等),直接初始化与拷贝初始化差别可以忽略不计。

(2)对于类类型的变量(如string或其他自定义类型),直接初始化调用类的构造函数(调用参数类型最佳匹配的那个),拷贝初始化调用类的拷贝构造函数。

特别的,当对类类型变量进行初始化时,如果类的构造函数采用了explicit修饰而且需要隐式类型转换时,则只能通过直接初始化而不能通过拷贝初始化进行操作。

列表初始化

列表初始化是C++ 11 新引进的初始化方式,它采用一对花括号({})进行初始化操作。而在此之前,比如C++98/03 只有数组和POD类型才可以使用列表初始化。

到了C++ 11,能用直接初始化和拷贝初始化的地方都能用列表初始化,而且列表初始化能对容器进行方便的初始化,所以在新的C++标准中,推荐使用列表初始化的方式进行初始化。

而在某些情况下,初始化的真实含义依赖于初始值时用的是花括号还是圆括号。

代码如下:

int main(void)
{
	vector<int>vec1(10);//vec1有10个元素,每个元素值为0
	vector<int>vec2{10};//vec2有1个元素,值为10
	vector<int>vec3(10, 1); // vec3有10个元素,每个元素值为1
	vector<int>vec4{ 10, 1 };// vec4有2个元素,值分别为10和1
}
  • 如果使用圆括号,提供的值是用来构造对象的
  • 如果使用花括号,表明我们相要列表初始化该对象,即尽可能把花括号中的值当成元素初始值来处理。
  • 但是如果提供的值不能用来列表初始化,则考虑通过构造来进行初始化vector<string>vec5{10};由于花括号里的值与元素类型不同,不能进行列表初始化,所以将vec5有10个元素,每个元素进行默认初始化。

当用于内置类型的变量时,这种初始化形式还有一个重要的特点:如果使用列表初始化且初始值存在丢失信息的风险,则编译器将报错

int main(void)
{
	double d = 1.2345;
	int a(d), b = d;		//正确;虽然编译器不报错,但是会提示存在丢失信息的风险
	int x{ d }, y = { d };//错误;编译器会报错,因为从double转换到int存在丢失信息的风险。
}

默认初始值

内置类型的初始值由定义的位置决定

  • 定义在任何函数体之外的变量被初始化为0
  • 定义在函数体内部的局部变量则未定义,如果试图拷贝或者以其他形式访问该变量,则会引发错误
  • 但是函数体内部的局部静态变量例外,如果局部静态变量没有显示的初始化,它将执行值初始化
int global_a;//值为0
short c;//值为0
int main(void)
{
	static int a;//值为0
	int b;//错误;未进行初始化
}

对于类类型,其默认初始值由类自己决定

  • 如果类中的数据成员在默认构造函数中进行了赋值,则默认初始化值优先使用默认构造函数的值
  • 如果在默认构造函数中没有赋值,但是该数据成员提供了类内初始值,则创建对象时,其默认初始值就是类内初始值,
  • 如果在默认构造函数中没有赋值且没有类内初始值,对于内置类型,则其值未定义,对于类类型,则对其进行默认初始化
class B {
public:
	string str="123";
};
class A {
public:
	A() {
		x = 1;
	}
	int x = 5;
	int y = 10;
	B b;
};
A global_a;
int main(void)
{
	A a;
	cout << global_a.x << " " << global_a.y << " " << global_a.b.str << endl;
	cout << a.x << " " << a.y << " " << a.b.str << endl;
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++变量初始化形式及其默认初始值问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++变量初始化形式及其默认初始值问题

这篇文章主要介绍了C++变量初始化形式及其默认初始值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-02-10

C#中的变量初始化形式是什么

本篇内容主要讲解“C#中的变量初始化形式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#中的变量初始化形式是什么”吧!C# 中的变量初始化变量通过在等号后跟一个常量表达式进行初始化(赋值
2023-06-17

Python的类成员变量默认初始值的坑及怎么解决

这篇文章主要讲解了“Python的类成员变量默认初始值的坑及怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python的类成员变量默认初始值的坑及怎么解决”吧!类成员变量默认初始值的
2023-06-29

vue el-switch初始值(默认值)不能正确显示状态问题及解决

这篇文章主要介绍了vue el-switch初始值(默认值)不能正确显示状态问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

基于java变量和作用域以及成员变量默认初始化的示例分析

这篇文章主要为大家展示了“基于java变量和作用域以及成员变量默认初始化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“基于java变量和作用域以及成员变量默认初始化的示例分析”这篇文章
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录