【C++】核心编程——04类和对象【对象的初始化和清理】

2 对象的初始化和清理

2.1 构造函数和析构函数

  对象的初始化和清理是非常重要的安全问题,C++利用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此 如果我们不提供构造和析构函数,编译器会提供。并且编译器提供的构造函数和析构函数是空实现。
  构造函数: 创建对象时,为对象的成员属性赋值,构造函数由编译器自动调用,无需手动实现。
  析构函数: 对象销毁前系统自动调用,执行一些清理工作。

  构造函数语法:类名(){}
  (1)构造函数,没有返回值也不写void
  (2)函数名称和类名相同
  (3)构造函数可以有参数,因此可以发生重载
  (4)程序在创建对象是会自动调用构造函数,无需手动调用,而且只会调用一次

  构造函数语法:~类名(){}
  (1)析构函数,没有返回值也不写void
  (2)函数名称和类名相同,在名称前加~符号
  (3)析构函数不可以有参数,因此不可以发生重载
  (4)程序在销毁对象前会自动调用析构函数,无需手动调用,而且只会调用一次

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

//对象的初始化和清理
//1.构造函数:进行初始化操作
//2.析构函数:进行清理操作
class Person
{
public:
//1.1 构造函数没有返回值也没有void
//1.2 构造函数的函数名同类名相同
//1.3 构造函数可以有参数,因此可以发生重载
//1.4 程序在创建对象时会自动调用构造函数,而且只会调用一次
	Person()
	{
		cout << "构造函数的调用" << endl;
	}
//2.1 析构函数没有返回值也没有void
//2.2 析构函数的函数名与类名同名,但在函数名之前加~
//2.3 析构函数没有参数,不可以重载
//2.4 析构函数在销毁对象前系统调用,并且只会调用一次
	~Person()
	{
		cout << "析构函数的调用" << endl;
	}
};
//构造函数和析构函数都是必须有的实现,如果程序员不提供,编译器会提供,并且是空实现
void test01()
{
	Person p1;//在栈上的数据,test01()函数执行完后,调用析构函数清理对象
}
int main()
{
	test01();
}

2.2 构造函数的分类和调用

  两种分类方式:
  (1)按参数分:有参构造和无参构造
  (2)按类型分:普通构造和拷贝构造
  三种调用方式:
  (1)括号法
  (2)显示法
  (3)隐式转换法

#include <iostream>
#include <string>
using namespace std;
//构造函数的作用:创建对象时为对象的成员属性赋值

//1.构造函数的分类
class Person
{
public:
	int age;

	//1.1 构造函数的分类:有参构造(默认构造函数),无参构造
	Person()//无参构造(普通构造)
	{
		cout << "Person的无参构造函数调用" << endl;
	}
	Person(int a)//有参构造(普通构造)
	{
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	//1.2 构造函数的分类:普通构造函数(上述都是普通构造函数),拷贝构造函数
	Person(const Person &p)//拷贝构造:把同类的对象的引用传入构造函数,并且该对象不能修改
	{
		//将传入的对象的所有属性,拷贝到本类
		age = p.age;
		cout << "Person的拷贝构造函数调用" << endl;
	}

};

 //2.构造函数的调用
void test01()
{
	//2.1 括号法
	cout << "括号法" << endl;
	Person p1;//默认构造
	Person p2(10);//有参构造
	Person p3(p2);//拷贝构造
		//注意:调用默认构造函数的时候,不要加():Person p1();会认为是函数声明,不会认为是创建对象
		
	//2.2 显示法
	cout << "显示法" << endl;
	Person p4;
	Person p5 = Person(10);//显示调用有参构造
	Person p6 = Person(p5);//显示调用拷贝构造
		//Person(10)等,是匿名对象;特点是当前行执行结束后,系统回立刻回收掉匿名对象
		//注意:不要利用拷贝构造函数初始化匿名对象:编译器认为Person(p3)等价于Person p3,重定义;


	//2.3 隐式转换法
	cout << "隐式转换法" << endl;
	Person p7 = 10;//相当于Person p4=Person(10)

}
int main()
{
	test01();
}

2.3 拷贝构造函数调用时机

  拷贝构造函数调用时机:
  (1)使用一个已经创建完毕的对象来初始化一个新对象
  (2)值传递的方式给函数参数传值
  (3)以值方式返回局部对象

#include <iostream>
#include <string>
using namespace std;
//拷贝构造函数调用时机
//1.使用一个已经创建完毕的对象来初始化一个新的对象
//2.值传递的方式给函数参数传值
//3.值方式返回局部对象

class Person
{
public:
	int age;

	
	Person()//无参构造(普通构造)
	{
		cout << "Person的无参构造函数调用" << endl;
	}
	Person(int a)//有参构造(普通构造)
	{
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	Person(const Person& p)//拷贝构造:把同类的对象的引用传入构造函数,并且该对象不能修改
	{
		//将传入的对象的所有属性,拷贝到本类
		age = p.age;
		cout << "Person的拷贝构造函数调用" << endl;
	}

};

//1.使用一个已经创建完毕的对象来初始化一个新的对象
void test01()
{
	Person p1(10);//有参构造
	Person p2(p1);//拷贝构造
}


//2.值传递的方式给函数参数传值
void doWork(Person p)
{
}
void test02()
{
	
	Person p1;
	doWork(p1);//实参传递给形参的时候回调用拷贝构造函数:传递过去后会拷贝临时的副本
}

//3.值方式返回局部对象:
Person doWork02()
{
	Person p1;
	cout << "局部对象对地址" << &p1 << endl;
	return p1;
}
void test03()
{
	Person p = doWork02();
	cout << "局部对象拷贝了副本,副本地址为" << &p << endl;
}
int main()
{
	cout << "1.使用一个已经创建完毕的对象来初始化一个新的对象" << endl;
	test01();
	cout << "2.值传递的方式给函数参数传值" << endl;
	test02();
	cout << "3.值方式返回局部对象" << endl;
	test03();
}

2.4 构造函数调用规则

  默认情况下,C++编译器至少给一个类添加3个函数
  (1)默认构造函数(无参,函数体为空)
  (2)默认析构函数(无参,函数体为空)
  (3)默认拷贝构造函数,对属性进行值拷贝

  构造函数调用规则:
  (1)如果用户定义有参构造函数,C++不在提供默认无参构造函数,但是会提供默认拷贝构造函数
  (2)如果用户定义拷贝构造函数,C++不会再提供其他构造函数(类中用户不能只提供拷贝构造)

#include <iostream>
#include <string>
using namespace std;
//构造函数的调用规则
//1.创建一个类时,C++编译器会给每个类都添加至少3个函数
	//默认构造:空实现
	//默认析构:空实现
	//拷贝构造:值拷贝
//2.如果我们写了有参构造,编译器就不会再提供默认构造,依然提供拷贝构造
//3.如果用户定义拷贝构造函数,C++不会再提供其他构造函数
class Person
{
public:
	int age;
	Person(int a)//有参构造
	{
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
};


class Person2
{
public:
	int age;
	Person2(int a)//有参构造
	{
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	Person2(const Person2& p)//拷贝构造:把同类的对象的引用传入构造函数,并且该对象不能修改
	{
		age = p.age;
		cout << "Person2的拷贝构造函数调用" << endl;
	}
	
};
//1.创建一个类时,C++编译器会给每个类都添加至少3个函数
void test01()
{
	Person p(10);//有参构造

	Person p2(p);//调用默认拷贝构造
	cout << "p2的年龄" << p2.age << endl;
}
//2.如果程序员写了有参构造,编译器就不会再提供默认构造,依然提供拷贝构造
void test02()
{
	//Person p;//默认构造,因为写了有参构造,编译器不再提供默认构造
	//p.age = 18;

	Person p(18);//有参构造
	Person p2(p);//调用默认拷贝构造
	cout << "p2的年龄" << p2.age << endl;
}
//3.如果用户定义拷贝构造函数,C++不会再提供其他构造函数
void test03()
{
	//Person2 p;//默认构造,因为写了拷贝构造函数构造,编译器不再提供默认构造等其他构造函数
	//p.age = 18;
	Person2 p(12);
	Person2 p2(p);//调用默认拷贝构造
	cout << "p2的年龄" << p2.age << endl;
}
int main()
{
	cout << "1.创建一个类时,C++编译器会给每个类都添加至少3个函数" << endl;
	test01();
	cout << "2.如果程序员写了有参构造,编译器就不会再提供默认构造,依然提供拷贝构造" << endl;
	test02();
	cout << "3.如果用户定义拷贝构造函数,C++不会再提供其他构造函数" << endl;
	test03();
}

2.5 深拷贝与浅拷贝

  **浅拷贝:(编译器自动提供的拷贝构造函数)**简单的赋值拷贝操作——浅拷贝带来的问题是堆区的内存重复释放,引起错误。
在这里插入图片描述

  **深拷贝:(用户自定义拷贝构造函数)**在堆区重新申请空间,进行深拷贝操作
在这里插入图片描述
  如果成员属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的错误.

#include <iostream>
#include <string>
using namespace std;
//深拷贝与浅拷贝
class Person
{
public:
	int age;
	int* height;
	Person()//无参构造
	{
		cout << "Person的无参构造函数调用" << endl;
	}
	Person(int a,int h )//有参构造
	{
		age = a;
		height = new int(h);
		//new在堆区开辟内存空间,返回一个指针:堆区数据由程序员开辟,程序员释放
		cout << "Person的有参构造函数调用" << endl;
	}
	~Person()//析构函数
	{
		//程序员手动释放堆区的数据
		if (height != NULL) {
			delete height;
			height = NULL;
		}
		cout << "Person的析构函数调用" << endl;
	}
	Person(const Person& p)
	{
		//height=p.height;//浅拷贝,编译器默认实现的是该代码
		cout << "用户定义的拷贝构造函数,实现深拷贝" << endl;
		age = p.age;
		height = new int(*p.height);//在堆区重新开辟空间,避免浅拷贝的重复释放堆区内存空间出现错误
	}
};

void test01()
{
	Person p1(18,165);//有参构造
	cout << "p1的年龄" << p1.age <<"身高为"<<*p1.height <<endl;

	Person p2(p1);//调用拷贝构造(编译器提供的拷贝构造函数——浅拷贝,用户自定义的拷贝构造,实现深拷贝)
	cout << "p2的年龄" << p2.age << "身高为" << *p2.height << endl;
}

int main()
{
	test01();
}

2.6 初始化列表

  作用:C++提供了初始化列表语法,用来初始化属性(构造函数的主要用途也是给属性做初始化操作)
  语法:构造函数():属性1(值1),属性2(值2)。。。{}

#include <iostream>
#include <string>
using namespace std;
//深拷贝与浅拷贝
class Person
{
public:
	int A;
	int B;
	int C;
	//1.传统初始化操作:利用构造函数
	//Person(int a, int b,int c)//有参构造
	//{
	//	A = a;
	//	B = b;
	//	C = c;
	//}

	//2.初始化列表初始化操作
	//Person() :A(10), B(20), C(30) 
	//{
	//}
	Person(int a,int b,int c) :A(a), B(b), C(c)
	{
	}
};

void test01()
{
	//1.传统初始化操作:利用构造函数
	//Person p1(18, 165,16);
	//cout << "A=" << p1.A << endl;
	//cout << "B=" << p1.B << endl;
	//cout << "B=" << p1.B << endl;

	//2.初始化列表初始化操作
	//Person p;
	//cout << "A=" << p.A << endl;
	//cout << "B=" << p.B << endl;
	//cout << "C=" << p.B << endl;
	Person p(18, 165, 16);
	cout << "A=" << p.A << endl;
	cout << "B=" << p.B << endl;
	cout << "C=" << p.B << endl;
}

int main()
{
	test01();
}

2.7 类对象作为类成员

  C++类中的成员可以是另外一个类的对象,我们称该成员为对象成员

class A{}
class B
{
	A a;
}

  //构造函数:先调用对象成员的构造,在调用自身类的构造,
  //析构函数:先调用自身类的析构,再调用对象成员的析构

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

//类对象作为类成员
class Phone
{
public:
	string m_pname;
	Phone()
	{

	}
	Phone(string name)
	{
		m_pname = name;
		cout << "类Phone的有参构造函数" << endl;
	}
	~Phone()
	{
		cout << "类Phone的析构函数" << endl;
	}
};

class Person
{
public:
	string m_name;
	Phone m_phone;
	//隐式转换,Phone m_phone=phone
	Person(string name,string phone):m_name(name),m_phone(phone)
	{
		cout << "类Person的构造函数" << endl;
	}
	~Person()
	{
		cout << "类Person的析构函数" << endl;
	}
};

//构造函数:先调用对象成员的构造,在调用自身类的构造,
//析构函数:先调用自身类的析构,再调用对象成员的析构
void test01()
{
	
	Person pe("张三","苹果MAX" );
	cout << pe.m_name << "拿着" << pe.m_phone.m_pname << "手机" << endl;
}
int main()
{
	test01();
}

2.8 静态成员

  定义:静态成员就是在成员变量和成员函数前加上关键字static,成为静态成员
  静态成员分为:
  (1)静态成员变量
  1.1 所有对象共享同一份数据
  1.2在编译阶段分配内存
  1.3类内声明,类外初始化
  1.4访问静态成员变量的方式:1.通过对象访问;2.通过类名访问 类名::静态成员变量
  (2)静态成员函数
  2.1 所有对象共享同一个函数
  2.2 静态成员函数只能访问静态成员变量
  2.3访问静态成员函数的方式:1.通过对象访问;2.通过类名访问 类名::静态成员函数()
  2.4静态成员函数遵从访问权限(public,static,protrcted),既私有的类外依旧不能访问**

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

//静态成员函数
//1.所有对象共享一个函数
//2.静态成员函数只能访问静态成员变量
class Person
{
public:
	static int m_A;//静态成员变量
	int m_B;
	static void func()//静态成员函数
	{
		m_A = 100;//静态成员 函数可以访问静态成员变量
		// m_B = 100;//静态成员 函数不可以访问非静态成员变量
		cout << "static void func()调用" << endl;
	}
};

//访问静态成员函数:
void test01()
{
	//1.通过对象访问
	Person p;
	p.func();
	//2.通过类名访问
	Person::func();
}

int main()
{
	test01();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值