0
点赞
收藏
分享

微信扫一扫

C++小知识点(六)------C++ primer plus中文版(第六版)

星巢文化 2022-04-30 阅读 44
c++

1、类和动态内存分配

1.1、静态成员初始化

在知识点(四)中的3.2使用static修饰变量,成为静态成员变量,使得所有对象共享变量,但值得注意的是变量类型不仅为static,还是const类型,此时可以在类中声明并初始化,但当没有const修饰时,不可在类声明中初始化,因为初始化是方法文件,而不是头文件

静态成员变量初始化方法:
头文件中
class Student
{
private:
	char *m_name;
	int len;
	static int m_num;
public:
	Student();
	Student(const char *s);
	~Student();
	friend std::ostream & operator<<(std::ostream &os,const Student &stu);
};

cpp文件中
int Student::m_num = 0;			使用作用域解析运算法,但是不包含static

1.2、类的动态内存分配

上面的学生类中有一个char指针用来存储学生的姓名,若采用固定长度的数据会造成资源浪费,此时可以在构造函数中动态的分配内存来存储姓名,待该对象消失时,使用析构函数回收该部分内存即可,因此此时析构函数不可省略。

int Student::m_num = 26;			使用作用域解析运算法,但是不包含static

Student::Student()
{
	m_name = new char[4];
	strcpy_s(m_name,4 ,"C++");
	m_num++;
	cout << "default object created!" << endl;
}

Student::Student(const char *s)
{
	len = strlen(s);
	m_name = new char[len + 1];
	strcpy_s(m_name, len + 1, s);
	m_num++;
	cout << m_num << ":\"" << m_name << "\" object created by Ordinary constructor" << endl;
}

Student::~Student()
{
	cout << "\"" << m_name << "\" object deleted" << endl;
	m_num--;
	delete[]m_name;
}

std::ostream & operator<<(std::ostream &os, const Student &stu)
{
	os << stu.m_name << endl;
	return os;
}
int main()
{
	Student stu;
	Student fhl("xiaofang");
	return 0;
}

1.3、类的动态内存分配常见错误

现象:

1、对象的值传递
2、对象的直接赋值
3、以值方式返回对象(非引用方式)
上述三种操作都会调用默认拷贝构造函数,生成一个拷贝对象,在对拷贝对象析构的时候会直接影响源对象的析构函数(因为析构函数中包含delete,即造成对同一片空间的二次delete)。

void callme(Student stu)			值传递
{
	cout << stu;
}

int main()
{
	Student stu;
	Student fhl("xiaofang");
	callme(fhl);					报错
	Student fhy = stu;				报错
	return 0;
}

解决方法一:自定义拷贝构造函数(复制构造函数)
了解:深拷贝和浅拷贝的区别

1、	拷贝构造函数原型:
Class_name(const Class_name & Type);

2、	何时调用拷贝构造函数:
	对象间的赋值操作:Student fhl(fhy);
	值传递对象:callme(fhl);
	编译器生成临时对象时(值方式返回对象)
	对已有对象的new操作

3、	默认的拷贝构造函数(浅拷贝)
	生成一个拷贝对象,逐个复制每个非静态成员,因此也称为浅拷贝

4、	自定义拷贝构造函数(深拷贝):重新申请内存空间,避免两次delete同一块内存
	复制的是源对象指针指向的数据,而不是单纯的复制指针,此时在析构delete时,会释放不同的内存空间
	class Student
	{
	private:
		char *m_name;
		int len;
		static int m_num;
	public:
		Student();							默认构造函数
		Student(const char *s);				普通构造函数
		Student(const Student &stu);		拷贝构造函数
		~Student();
		friend std::ostream & operator<<(std::ostream &os,const Student &stu);
	};

	Student::Student(const Student &stu)
	{
		len = strlen(stu.m_name);
		m_name = new char[len + 1];
		strcpy_s(m_name, len + 1, stu.m_name);
		m_num++;
		cout << m_num << ":\"" << m_name << "\" object created by Copy constructor" << endl;
	}

解决方法二:赋值运算符重载:不创建新对象
并不是所有的问题都归咎于复制构造函数,赋值运算符同样需要注意:

Student fhy = stu;	可能是先调用复制构造函数创建临时对象,然后通过默认赋值对成员逐个复制
但是我通过visual studio 2017实测发现,并不会调用

class Student
{
private:
	char *m_name;
	int len;
	static int m_num;
public:
	Student();									//默认构造函数
	Student(const char *s);						//普通构造函数
	Student(const Student &stu);				//拷贝构造函数
	Student & operator=(const Student &stu);	//赋值运算符重载
	~Student();
	friend std::ostream & operator<<(std::ostream &os,const Student &stu);
};

Student & Student::operator=(const Student &stu)
{
	if (this == &stu)		//防止对象的自我复制
		return *this;
	delete[]m_name;			//释放=左边对象原有内存中的数据,不释放的会浪费此块内存,下次new分配的是一块新空间
	len = stu.len;
	m_name = new char[len + 1];
	strcpy_s(m_name, len + 1, stu.m_name);
	cout << "调用赋值构造函数!" << endl;
	return *this;
}

总结:

在知识点(三)3.3中说:一般提供两个构造函数,一个析构函数,但是到现在需要加上一个:

一般提供三个构造函数(默认构造、普通构造、复制构造函数),一个析构函数

2、其他知识点

2.1、静态成员函数

前有静态成员,现有静态成员函数,在成员函数前加上static变成静态成员函数。

class Student
{
private:
	char *m_name;
	int len;
	static int m_num;
public:
	Student();									//默认构造函数
	Student(const char *s);						//普通构造函数
	Student(const Student &stu);				//拷贝构造函数
	Student & operator=(const Student &stu);	//赋值运算符重载
	static int Number() {return m_num;}			//静态成员函数
	~Student();
	friend std::ostream & operator<<(std::ostream &os,const Student &stu);
};

访问方法:
int count = Student::Number();

同静态成员变量一样,不属于某个对象,所有对象共享一个函数,因此只能访问静态成员变量

2.2、返回对象

1、	返回指向const对象的引用,不会调用复制构造函数
2、	返回非const对象的应用:赋值运算符重载(返回的是指向调用对象的引用)
3、	返回局部对象,不要使用引用方式返回,因为函数执行完毕,引用指向的对象将不存在,而直接返回对象会调用复制构造函数

总之
1、如果方法或函数要返回局部对象,则应返回对象,而不是指向对象的引用。在这种情况下,将使用复制构造函数来生成返回的对象。
2、如果方法或函数要返回一个没有公有复制构造函数的类(如ostream类)的对象,它必须返回一个指向这种对象的引用。
3、有些方法和函数〈如重载的赋值运算符)可以返回对象,也可以返回指向对象的引用,在这种情况下,应首选引用,因为其效率更高。

2.3、指向对象的指针

1、首先创建对象数组,使用对象指针指向数组成员,即对象指针
2、使用new为对象分配内存

int main()
{
	Student stu[2] = {
		Student("xiaofang"),
		Student("xiaowu")
	};
	Student *first = &stu[0];							//对象指针
	Student *second = &stu[1];
	Student *third = new Student(stu[1]);				//已有对象的new操作,将调用拷贝构造函数
	cout << *first << *second << *third << endl;		//<<运算符重载
	delete third;
	return 0;
}	

2.4、对象和指针

1、	对象指针的初始化
	Student *first = &stu[0];				//初始化为已有对象
	Student *third = new Student(stu[1]);	//指针初始化并指向创建的新对象

2new和构造函数的搭配
	Student *temp = new Student;						//调用默认构造函数
	
	Student *temp = new Student("xiaofang");			//调用普通构造函数
	
	Student *temp = new Student(stu[0]);				//调用拷贝构造函数
举报

相关推荐

0 条评论