2021-10-25

本文详细介绍了C++中的构造函数,包括转换构造函数和构造函数初始化列表的重要性,强调了const成员、引用成员及对象成员的初始化规则。同时,探讨了赋值与初始化的区别,并展示了拷贝构造函数的功能和调用场景。此外,还讨论了对象的作用域、生存期以及static成员的使用,包括其在单例模式中的应用。最后,提到了const成员函数的角色和mutable关键字的用途。
摘要由CSDN通过智能技术生成

提示:上节课内容,构造函数是一种特殊的成员函数,在创建对象的时候自动调用,对对象的数据成员进行初始化。

一、转换构造函数

1.构造函数的作用  初始化和类型转化(转化构造函数)
  转化构造函数:带一个参数的构造函数,能够将其他类型转换为类类型。
2.类的构造函数只有一个参数是非常危险的,因为编译器可以使用这种构造函数把参数的类型隐式转换为类类型。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、赋值与初始化的区别

Test& operator=(const Test& other);

Test& Test:: operator =(const Test& other)
{
	num_ = other.num_;
	cout<<"Test:: operator" << endl;
	return *this;//返回对象自身

}

int main(void)
{
	/*Test t(10);

	t = 20;
	Test t2;*/
	Test t = 10;//这个时候的=运算符不是运算符,这里等价于Test t(10),表示初始化
	t = 20;//真正的赋值操作
	//重载一个等号运算符


	return 0;
}

在这里插入图片描述

注:explicit关键字,表示只提供给类的构造函数使用的关键字,编译器不会把声明为explicit的构造函数用于隐式转换,能够在程序代码中显示创建对象。

explicit Test(int num);

在这里插入图片描述
现在代码就不能进行隐式转换了

三、(构造函数与析构函数第三节内容)

1.构造函数初始化列表

什么是构造函数初始化列表呢?
举例如下:
在这里插入图片描述

2.对象成员及其初始化

对数据成员的初始化推荐放在初始化列表当中,包括普通数据成员和对象数据成员。
如果对象所对应的类没有构造函数,初始化一定要放在初始化列表当中。如果有默认构造函数,可以省略

代码如下(示例):

#include<iostream>
using  namespace std;

class Object
{
public:
	Object(int num) : num_(num)
	{
		cout << "Object…"<<num_ << endl;
	}
	~Object()
	{
		cout << "~Object…" <<num_<< endl;
	}
private:
	int num_;
};
class Container
{
public:
	Container(int obj1=0,int obj2=0) : obj1_(obj1),obj2_(obj2)//与这里的顺序无关
	{
		cout << "Container" << endl;
	}
	~Container()
	{
		cout << "~Container" << endl;
	}
private://类实例化相当于一块儿内存
	Object obj1_;//与初始化定义的顺序有关obj1_的内存位置在前面
	Object obj2_;
};

int main(void)
{//实例化一个Container对象
	Container c(10,20);
	return 0;
}

在这里插入图片描述

3.const 成员、引用成员初始化

const成员的初始化,只能在构造函数初始化列表中进行;
引用成员的初始化也只能在构造函数初始化列表中进行;
对象成员(对象所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行。
#include<iostream>
using  namespace std;

class Object
{
public:
	Object(int num=0) : num_(num),knum_(100)
	{
		//knum_ = 100;Error 常量的初始化,必须在初始化列表中进行初始化
		//本质上不是初始化,而是赋值操作
		//refnum_ = num Error 引用成员的初始化也必须放入初始化列表中
		cout << "Object…" << num_ << endl;
	}
	~Object()
	{
		cout << "~Object…" << num_ << endl;
	}
private:
	int num_;
	const int knum_;
	int& refnum_;//引用成员
};

int main(void)
{
	Object obj(10);
	return 0;
}

在这里插入图片描述

#include<iostream>
using  namespace std;

class Object
{
public:
	enum E_TYPE
	{
		TYPE_A = 100, TYPE_B = 200
	};
public:
	Object(int num = 0) : num_(num), knum_(100),refnum_(num_)
	{
		//knum_ = 100;Error 常量的初始化,必须在初始化列表中进行初始化
		//本质上不是初始化,而是赋值操作
		//refnum_ = num Error 引用成员的初始化也必须放入初始化列表中
		cout << "Object…" << num_ << endl;
	}
	~Object()
	{
		cout << "~Object…" << num_ << endl;
	}
	void DisplayKnum()
	{
		cout << "knum=" << knum_ << endl;
	}
private:
	int num_;
	const int knum_;//常量局限于对象内部是常量,为了使它对所有对象都是常量,只能用枚举来实现
	int& refnum_;//引用成员
};

int main(void)
{
	Object obj1(10);
	Object obj2(20);//定义两个对象,意味着
	obj1.DisplayKnum();
	obj2.DisplayKnum();

	cout << obj1.TYPE_A << endl;
	cout << obj2.TYPE_A << endl;
	cout << Object::TYPE_A << endl;
	return 0;
}

在这里插入图片描述

四、(构造函数与析构函数第四节内容)

1.拷贝构造函数

功能:使用一个已经存在的对象来初始化一个新的同一类型的对象
声明:只有一个参数并且参数为该类对象的引用
如果类中没有说明拷贝构造函数,则系统自动生成一个缺省复制构造函数,作为该类的公有成员
Test(const Test& other);

Test::Test(const Test& other)
{
	num_ = other.num_;//可以这样初始化(相当于赋值),也可以放在初始化列表中
	cout<<"Inicializing with other"<<num_ << endl;
}

int main(void)
{
	Test t(10);
	//拷贝对象初始化是用一个对象初始化另一个同种对象
	Test t2(t);//调用拷贝构造函数,未定义,提供系统默认拷贝构造函数
	return 0;
}

说明:为什么拷贝构造函数参数必须是对象的引用?非法的。引用不创建地址空间,与实参共享同一块地址空间。不会再构造一个对象,使用引用减少内存复制

在这里插入图片描述

2. 拷贝构造函数调用的几种情况

1. 当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。
2. 当函数的返回值是类对象,函数执行完成返回调用者时使用。理由也是要建立一个临时对象中,再返回调用者。为什么不直接要用返回的局部对象呢?因为局部对象在离开建立它的函数时就消亡了,不可能在返回点用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。

五、对象的使用

1.static成员独立于对象而存在,也就是说它不属于某个对象的成员,能被全体对象所共享
2.统计类类型对象创建的个数  用static成员来实现
3.非static成员它属于类对象,每个对象都有一份拷贝
4.static成员函数没有this指针,不能访问非static成员,也不能调用非static成员函数

提示:静态成员的展示

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year) :year_(year)
	{

	}
	//仅仅想判定年份,不需要创建对象,那么可以创建一个静态成员函数
	//如果没加static一定需要定义一个对象
	static bool IsLeapYear(int year)
	{
		return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
	}
	bool IsLeapYear()
	{
		return (year_ % 4 == 0 && year_ % 100 != 0) || (year_ % 400 == 0);
	}
private :
	int year_;
};
int main(void)
{
	Date d(2012);
	cout << d.IsLeapYear() << endl;
	cout <<Date:: IsLeapYear(2010) << endl;
	return 0;
}

在这里插入图片描述

1.四种对象的作用域与生存期

1.栈对象(隐含调用构造函数,程序中没有显示调用)
2.堆对象(隐含调用构造函数,程序中没有显示调用,需要显示释放)
3.全局对象、静态全局对象
  全局对象的构造先于main函数
  **已初始化的全局变量或静态全局对象存储于.data段中
  未初始化的全局变量或静态全局对象存储与.bss段中**
4.静态局部对象
⭐作用域不等同于生存期

提示:栈对象、堆对象代码展示

#include<iostream>
using namespace std;

class Test
{
public:
	Test(int n):n_(n)
	{
		cout << "Test"<<n_<<"…" << endl;
	}
	~Test()
	{
		cout << "~Test" << n_ << "…" << endl;
	}
private:
	int n_;
};
Test g(100);//全局对象的构造先于main函数
static Test g2(200);//静态全局对象的构造先于main函数

int main(void)
{
	cout << "Entering main…" << endl;
	//cout << n << endl;未初始化的全局变量,初始值为0(在我的编译器是error)
	Test t(10);//栈对象,特征是构造函数隐含被调用。生存期结束的时候自动释放
	{
		Test t(20);//作用域范围不同,可以不同的
	}
	{

		Test* t3 = new Test(30);//堆对象,不能够自动被释放
		delete t3;//需要显式释放
	}
	{
		static int n3;//n3存储于.bss段中    (编译期初始化)
		static int n4 = 100;//n4存储于.data段中(编译期初始化)

		static Test t4(333);//类对象,在运行期初始化,存储于.data段中
	}
	cout << "Exiting main…" << endl;

}

在这里插入图片描述
在这里插入图片描述
提示:.bss未初始化的数据段在可执行文件中不占用空间

2.static用法总结

1.用于函数内部修饰变量,静态变量,这种变量的生存期长于该函数,使得函数具有一定的状态。使静态变量一般是不可重入的,也不是线程安全的
2.用在文件级别,修饰变量或函数,表示该变量或函数只在本文件可见
3.C++中
用于修饰类的数据成员,所谓“静态成员”。这种数据成员的生存期大于class的对象
用于修饰class的成员函数,即“静态成员函数”。这种成员函数只能访问静态成员和其他静态成员函数,不能访问非静态成员和非静态成员函数。

3.static与单例模式

单例模式是一种设计模式(软件工程的用例),禁止拷贝

#include<iostream>
using namespace std;
class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (instacne_ == NULL)
		{
			instacne_ = new Singleton;//保证多次调用返回同一个对象
		}
		return instacne_;
	}
	~Singleton()
	{
		cout << "~Singleton…" << endl;
	}
	/*
	static void Free()
	{
		if (instacne_ !=  NULL)
		{
			delete instacne_;
		}
	}
	*/
	class Garbo
	{
	public:
		~Garbo()
		{
			if (Singleton::instacne_ !=NULL)
			{
				delete instacne_;
			}
		}
	};
private:
	Singleton()
	{
		cout << "Singleton…" << endl;
	}
	static Singleton* instacne_;
	static Garbo garbo_;//利用对象的确定性析构
};
Singleton::Garbo Singleton::garbo_;
Singleton* Singleton::instacne_;
int main(void)
{
	//Singleton s1;
	//Singleton s2;
	Singleton*s1= Singleton::GetInstance();
	Singleton*s2 = Singleton::GetInstance();
	//Singleton s3(*s1);调用拷贝构造函数
	//Singleton::Free();对资源进行释放

	return 0;
}

在这里插入图片描述

六、const成员函数

  • const成员函数不会修改对象的状态
  • 定义一个常量时必须初始化
  • const成员函数只能访问数据成员的值,而不能修改它
  • 用mutable修饰的数据成员即使在const对象或在const成员函数中都可以被修改
  • 在类中,如果有const成员,const成员的初始化只能在构造函数初始化列中进行

提示:下节运行实例理解数据抽象与封装(进度拉满能不能,我这太想学完刷leetcode了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值