IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++赋值和初始化的区别 -> 正文阅读

[C++知识库]C++赋值和初始化的区别

C++中对变量的操作有很多,比如赋值、初始化等。很多人都会把赋值和初始化混为一谈,下面我们就来研究一下赋值和初始化的区别。先上结论:

初始化赋值
在变量生命周期中只能进行一次可以无限次数进行
任何类型的变量都必须初始化(基本类型无初值也可以视为调用默认构造函数,而对象必须调用构造函数进行初始化,无构造函数会调用默认构造函数,有构造函数必须调用其中一个,调用无参构造函数时可以省略括号,这也算初始化)可有可无
必须在声明时紧跟在变量名后可以在变量生命周期中的任何位置
在类的构造函数里初始化必须写在初始化列表中可以在类的构造函数里的任何位置
可以使用() ,也可以使用=(如果是自定义类型,必须满足构造函数只有一个参数且类的构造函数没有显式指定不能使用=)狭义的赋值只能使用=(重载运算符)
引用类型初始化是指定指向的变量引用类型的赋值视为对原变量的赋值
初始化中的=或()不应视为运算符,只能视为一种标志,因此它没有返回值(不是返回值为void类型,而是直接没有,就像一个break语句没有返回值那样)基本类型返回自身的引用,自定义类型可以根据需要修改,一般也返回自身引用

下面,我们通过几个具体例子来说明赋值和初始化的区别。
例1

int main()
{
	int a = 5;//初始化
	a = 3;//赋值
	return 0;
}

这个例子虽然简短,但却显示出初始化与赋值在语法上的区别。当然,这并不足以说明初始化和赋值实际的区别。
例2

#include<iostream>
void myfunc()
{
	static int a = 0;//静态变量初始化,只执行一次
	static int b;//静态变量
	b = 0;//变量的赋值,每次调用函数都被执行
	a++;
	b++;
	std::cout << a << ' ' << b << std::endl;
}
int main()
{
	for (int i = 0; i < 10; i++)
		myfunc();
	return 0;
}

运行结果:
运行结果
这里的a和b都是静态变量,为什么结果会不同?原因就在于a=1是初始化,在a的生命周期(从第一次调用到程序结束)里只会执行一次,而b=1是赋值,不属于变量声明代码,所以每次调用都会被执行。
例3

#include<iostream>
class MyClass
{
public:
	MyClass()
	{
		std::cout << "无参构造函数被调用" << std::endl;
	}
	MyClass(int x)
	{
		std::cout << "有参构造函数被调用" << x << std::endl;
	}
	void operator=(int x)
	{
		std::cout << "operator=被调用" << x << std::endl;
	}
};
void func(int t)
{
	std::cout << "第" << t << "次调用func" << std::endl;
	static MyClass a = 10;//静态变量初始化,隐式调用构造函数,在整个程序里只执行一次,不是赋值!
	static MyClass b;//静态变量初始化,调用无参构造函数
	b = 20;//赋值,调用operator=,和初始化不一样!
	MyClass c(10);//普通变量初始化,隐式调用构造函数
	MyClass d;//普通变量初始化,调用无参构造函数
	d = 10;//赋值
}
int main()
{
	for (int i = 0; i < 5; i++)
	{
		func(i);
		std::cout << std::endl;
	}
	return 0;
}

运行结果:
运行结果
通过本例可以看出,静态变量的初始化只执行一次,普通变量的初始化在每个生命周期都有一次,而所有变量赋值操作每次调用函数都会执行。
例4

#include<iostream>
class MyClass
{
public:
	MyClass()
	{
		std::cout << "无参构造函数被调用" << std::endl;
	}
	MyClass(int x)
	{
		std::cout << "有参构造函数被调用" << x << std::endl;
	}
	void operator=(int x)
	{
		std::cout << "operator=被调用" << x << std::endl;
	}
};
class Test
{
private:
	MyClass obj;
public:
	Test()
	{
		std::cout << "在构造函数中不初始化:" << std::endl;
		std::cout << std::endl;
	}
	Test(int x)
	{
		std::cout << "在构造函数中赋值:" << std::endl;
		obj = x;//赋值
		std::cout << std::endl;
	}
	Test(int x, int y) :obj(x)//初始化
		//设置y只是为了实现重载
	{
		std::cout << "在构造函数中初始化:" << std::endl;
		std::cout << std::endl;
	}
};
int main()
{
	Test a;
	Test b(7);
	Test c(5, 0);
	return 0;
}

运行结果:
运行结果
通过这个例子,我们能得出两个结论:
1.构造函数初始化列表中的成员会被初始化,不在初始化列表中的成员会被调用无参构造函数,如果某成员没有无参构造函数且不在初始化列表,会报错;
2.成员的初始化在构造函数中任何代码之前被执行。
通过结论1,我们可以得出,在构造函数中如果想设置成员变量,初始化比赋值速度快,因为赋值之前会先调用一遍构造函数,如果不在初始化列表中进行初始化,就会造成重复调用,引起时间浪费。
例5

#include<iostream>
class MyClass
{
public:
	MyClass()
	{
		std::cout << "无参构造函数被调用" << std::endl;
	}
	MyClass(int x)
	{
		std::cout << "有参构造函数被调用" << x << std::endl;
	}
	void operator=(int x)
	{
		std::cout << "operator=被调用" << x << std::endl;
	}
};
class Father
{
protected:
	MyClass obj;
public:
	Father()
	{
		std::cout << "父类无参构造函数" << std::endl;
		std::cout << std::endl;
	}
	Father(int x) :obj(x)//初始化
	{
		std::cout << "父类有参构造函数" << std::endl;
		std::cout << std::endl;
	}
};
class Son :public Father
{
private:
	MyClass son_obj;
public:
	Son()
	{
		std::cout << "子类无参构造函数" << std::endl;
		std::cout << std::endl;
	}
	Son(int x) :son_obj(x)
	{
		std::cout << "子类单参构造函数" << std::endl;
		std::cout << std::endl;
	}
	Son(int x, int y) :/*obj(x),不能这样初始化*/Father(x),son_obj(y)
	{
		std::cout << "子类双参构造函数1" << std::endl;
		std::cout << std::endl;
	}
	Son(int x, int y, int i) :Father(x)//设置i是为了实现重载
	{
		son_obj = y;
		std::cout << "子类双参构造函数2" << std::endl;
		std::cout << std::endl;
	}
};
int main()
{
	Father a;
	std::cout << "-------------------------" << std::endl;
	Father b(1);
	std::cout << "-------------------------" << std::endl;
	Son c;
	std::cout << "-------------------------" << std::endl;
	Son d(2);
	std::cout << "-------------------------" << std::endl;
	Son e(3, 4);
	std::cout << "-------------------------" << std::endl;
	Son f(5, 6, 0);
	return 0;
}

运行结果:
运行结果
本例显示出有继承的初始化顺序:无论如何,先初始化父类,然后初始化子类初始化列表里的成员,最后执行子类构造函数中的代码。另外提一点:初始化列表里的顺序和实际初始化的顺序没有关系!!!实际初始化的顺序是按照声明顺序来初始化的,不是按照初始化列表的顺序来初始化的!!!
讲到这里,相信大家能够分辨并合理使用赋值和初始化了。今天先讲到这里,我们下期再见!

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-13 12:37:21  更:2021-12-13 12:38:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:34:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码