类和构造和析构基础知识总结 (学习笔记)

本文详细介绍了C++中的类概念,包括类的属性权限、构造函数(默认和自定义)、析构函数的作用,以及拷贝构造函数(浅拷贝与深拷贝的区别)。
摘要由CSDN通过智能技术生成

正片开始

C++中的类

注意要点:
  1. 当类中没有任何数据时,此时类所占大小为1个字节
  2. 在类中引入了权限限定词如:public(公有属性),protected(保护属性),private(私有属性)
  3. 当类中的数据没有被权限限定时,默认为私有属性
  4. 当类中的成员函数访问类中的数据时无权限,可以随意访问
  5. 在类外访问类中数据时只能访问公有属性
如何创建类,并访问类中的数据

什么是类呢?其实就是结构体换了个关键字,将struct 换成了class,很简单吧!
让我们看看用代码如何创建一个类

#include<iostream>
#include<string>
using namespace std;

class MM
{
	int age = 18;   //无权限限定默认为私有属性
public:    //公有属性
	void print_data()
	{
		cout << "姓名:" << name << endl << "年龄:" << age << endl << "身份证号:" << ID << endl << "地址:" << pos << endl<<"家产:"<<money<<endl;
	}
	string explain = "我是公有属性,可以直接访问";
protected:   //保护熟悉
	string ID = "5155354512542153";
	string name = "小明";
	string pos = "四川";
private:    //私有熟悉
	string money = "10000元";

};

int main()
{
	MM mm;
	//公有属性访问
	cout << mm.explain << endl;
	mm.print_data();
	//下面这种是错误写法,不是公有属性无法直接访问
	//mm.age;
	//mm.ID;
	//mm.name;
	//mm.pos;
	//mm.money;
	return 0;
}
输出结果
我是公有属性,可以直接访问
姓名:小明
年龄:18
身份证号:5155354512542153
地址:四川
家产:10000

C++中的构造函数

注意要点:

  1. 构造函数的名字必须和类名相同,且无需返回值,参数随意
  2. 构造函数是用来创建对象的,所以一般情况属于public(公有属性)
  3. 不写构造函数存在一个默认的无参构造函数,写了任何形式的构造函数,则默认的构造函数不存在
  4. 创建对象时,程序自己调用构造函数,如果构造函数带参,则创建对象时也必须带参
  5. 构造函数也满足函数缺省以及函数重载功能,在类中声明类外实现与结构体和空间命名规则一样返回类型 类名::函数名(参数){函数体};
构造函数中的关键字default和delete
  1. default
class MM
{
	public:
		MM()=default //我自己创建了个构造函数,用default告诉程序我要使用默认的构造函数,此时程序会调用默认的构造函数,比自己写的构造函数效率快。		
};
  1. delete
class MM
{
	public:
		MM()=delete;//删除默认的构造函数,此时这个类已经无法创建变量
};
int main()
{
	MM mm; //此变量已无法创建
}

在这里插入图片描述

构造函数的实现
#include<iostream>
#include<string>
using namespace std;

class GG
{
	public:
		GG(int m_age,string m_name)
		{
			age=m_age;
			name=m_name;
			cout<<"构造函数已被调用"<<endl;			
		}
		void print_data()
		{	
			cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl;
		}
	protected:
		int age;
		string name;	
};

int main()
{
	//此时自己写了有参构造函数,创建变量的方式发生变化
	GG gg(18,"人妖");  //创建此变量时会自动调用构造函数
	gg.print_data();
	return 0;
}
输出结果
构造函数已被调用
年龄:18
姓名:人妖

析构函数

如果在类中有指向(自由存储区/堆区)的指针,则需要自己写一个析构函数来释放这块空间,要不然会存在内存泄漏。

#include<iostream>
#include<string>
using namespace std;

class GG
{
public:
	GG(const char* str) //构造函数
	{
		int len = sizeof(str); //计算字符串的个数
		p_str = new char[len];
		strcpy(p_str, str);
	}
	void print_data()
	{
		cout << p_str << endl;
	}
	~GG()  //析构函数
	{
		delete[] p_str;
		cout << "已释放完毕!" << endl;
	}
protected:
	char* p_str;
};

int main()
{
	//程序死亡之前,系统会自己调用析构函数释放空间
	GG gg("武大郎");
	gg.print_data();
	return 0;
}
输出结果
武大郎
已释放完毕!

构造和析构顺序问题

代码解释

#include<iostream>
#include<string>
using namespace std;

class DD
{
public:
	DD(string m_str = "A")
	{
		str = m_str;
		cout << m_str << " ";
	}
	~DD()
	{
		cout << str << " ";
	}
protected:
	string str;
};

int main()
{
	//构造函数和析构函数调用顺序相反
	
	{
		cout << "第一组测试:" << endl;
		cout << "构造:" << endl;
		DD d("Z");
		DD dd("X");
		DD ddd("C");
		cout << endl << "析构:" << endl;

	};
	cout << "\n\n";
	{
		cout << endl<< "第二组综合测试:" << endl;
		cout << "构造:" << endl;
		DD f("Q");
		static DD ff("W");
		DD* p = new DD("E");
		DD fff("R");
		delete p;
		DD ffff("T");   //输出结果 Q W E R E T   T R Q W
		cout << endl << "析构:" << endl;
	}
		
	return 0;
}

注意:在没有 “静态变量” 和 " 自由存储区变量" 时 构造函数和析构函数调用顺序是相反的,如上述第一组代码测试,如果有 " 自由存储区变量" 时 遇到delete就调用析构,记住如果有静态变量,则静态变量调用的析构函数永远是最后一个

拷贝构造函数

注意:拷贝构造函数的参数必须是引用,如果是传值则会无限递归下去
#include<iostream>
#include<string>
using namespace std;

class MM
{
public:
	MM(string _name = "张三", int _age = 18, string _sex = "人妖")
	{
		name = _name;
		age = _age;
		sex = _sex;
	}

	MM(const MM& param)
	{
		name = param.name;
		age = param.age;
		sex = param.sex;
	}
protected:
	string name;
	int age;
	string sex;
};

int main()
{
	MM m1;
	//拷贝的第一种写法
	MM m2(m1);
	//拷贝的第二种写法
	MM m3=m1;
	return 0;
}

在这里插入图片描述

可以看见在创建变量m2,m3时实现了将m1的数据拷贝给m2,m3的过程,在拷贝时会自动调用拷贝函数,如果自己没有写拷贝函数,系统会有默认的拷贝函数

浅拷贝
浅拷贝就是在类中由系统自带的默认拷贝构造函数,它会将一些简单的数据拷贝到另一个对象中,如果存在,在堆区和自由存储区开辟内存,当拷贝完时,两个对象都拥有了这块地址的指向,当释放内存时,会对这块内存多次释放,造成内存多次释放问题。
class MM
{
public:
	MM(const char* str)
	{
		len = sizeof(str); //计算字符串长度
		p_str = new char[len];
		strcpy(p_str, str);
	}
	//系统默认的拷贝构造函数功能实现
	MM(const MM&param)
	{
		len=param.len;
		p_str=param.p_str;  //此时m2对象中的p_str和m1中的p_str都指向了同一块内存,释放会出现多次释放问题
	}
	~MM()
	{
		if (p_str != NULL)
		{
			delete[] p_str;
			p_str = NULL;
		}
	}
	
protected:
	char* p_str;
	int len;
};

int main()
{
	MM m1("张三");
	MM m2(m1);
	return 0;
}
深拷贝
为了解决上述的情况,引入了深拷贝,就是将堆区或者自由存储区的空间大小重新开辟一份,将内容拷贝到新开辟的空间中,让m2中的指针指向新开辟的空间,这样就不会造成对同一块内存多次释放问题,代码实现如下
class MM
{
public:
	MM(const char* str)
	{
		len = sizeof(str); //计算字符串长度
		p_str = new char[len];
		strcpy(p_str, str);
	}

	MM(const MM& param)
	{
		len = param.len;
		p_str = new char[len];
		strcpy(p_str, param.p_str);
	}

	~MM()
	{
		if (p_str != NULL)
		{
			delete[] p_str;
			p_str = NULL;
		}
	}
	
protected:
	char* p_str;
	int len;
};

int main()
{
	MM m1("张三");
	MM m2(m1);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值