c++类和对象2——对象的初始化和清理

  1. 构造函数和析构函数

    生活中我们买的电子产品都会有出厂设置,在某一天我们不用时也会删除一些自己
    信息数据安全
    C++中的面向对象源于生活,每个对象也都会有初始化设置和清理数据设置
    
    对象的初始化和清理也是两个非常重要的安全问题
    一个对象或者变量没有初始状态,对其使用后果是未知
    同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
    
    c++利用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,
    完成对象初始化和清理工作
    对象的初始化和清理工作是编译器强制要我们做的事情,
    因此如果我们不提供构造和析构,编译器会提供
    编译器提供的构造函数和析构函数是空实现
    
    构造函数:主要作用于创建对象时为对象的成员属性赋值,
    构造函数由编译器自动调用,无需手动调用
    析构函数:主要作用于对象销毁前系统自动调用,执行一些清理工作
    
    构造函数语法:类名 () {}
      1.构造函数,没有返回值也不写void
      2.函数名称与类名相同
      3.构造函数可以有参数,因此可以发生重载
      4.程序在调用对象时会自动调用构造,无需手动调用,而且只会调用一次
      
    析构函数语法:~ 类名 () {}
      1.析构函数,没有返回值也不写void
      2.函数名称与类名相同,在名称前加上符号~
      3.析构函数不可以有参数,因此不可以发生重载
      4.程序在对象销毁前自动调用析构,无需手动调用,而且只会调用一次
    
#include <iostream>
using namespace std;

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

//对象的初始化和清理
 // 1.构造函数  进行初始化操作
class Person
{
    
    
public:
	//1.1、构造函数
	//构造函数,没有返回值也不写void
	//函数名称与类名相同
	//构造函数可以有参数,因此可以发生重载
	//程序在调用对象时会自动调用构造,无需手动调用,而且只会调用一次
	Person()
	{
    
    
		cout << "Person构造函数的调用" << endl;
	}

	//如果自己不写,编译器会自动写一个下面这样的
	/*Person()
	{
		
	}*/

	//2.析构函数  进行清理的操作
	//析构函数,没有返回值也不写void
	//函数名称与类名相同,在名称前加上符号~
	//析构函数不可以有参数,因此不可以发生重载
	//程序在对象销毁前自动调用析构,无需手动调用,而且只会调用一次
	~Person()
	{
    
    
		cout << "Person析构函数调用" << endl;
	}
 };

//构造和析构都是必须有的实现,如果我们自己不写,编译器会自动提供一个空实现
void test01()
{
    
    
	Person p; //局部变量 在栈区,test01()执行完毕后,就会释放这个对象
}
 
int main()
{
    
    
	test01();
	
	//Person p;
	
	system("pause");
	return 0;
}
  1. 构造函数的分类以及调用

    两种分类方式:
       按参数分为:有参构造和无参构造(默认构造)
       按类型分为:普通构造和拷贝构造
    
    三种调用方式:
           括号法
    	   显示法
    	   隐式转换法
    
#include <iostream>
using namespace std;

/*构造函数的分类及调用
  
  两种分类方式:
       按参数分为:有参构造和无参构造(默认构造)
	   按类型分为:普通构造和拷贝构造
  
  三种调用方式:
       括号法
	   显示法
	   隐式转换法*/

//分类
class Person
{
    
    
public:
	//构造函数
	Person()
	{
    
    
		cout << "Person的无参构造函数的调用" << endl;
	}

	Person(int a)
	{
    
    
		age = a;
		cout << "Person的有参构造函数的调用" << endl;
	}

	//拷贝构造函数
	Person(const Person &p)//理解不了先记着
	{
    
    
		//将传入的人身上的所有属性拷贝到我身上
		age = p.age;

		cout << "Person的拷贝构造函数的调用" << endl;
	}

	~Person()
	{
    
    
		cout << "Person析构函数调用" << endl;
	}

	int age;
};

//调用
void test01() 
{
    
    
	//1.括号法
	//Person p;//默认构造函数调用
	//Person p2(10);//有参函数调用
	//Person p3(p2);//拷贝构造函数

	//注意事项1
	//调用默认构造函数时候,不要加()
	//因为下面这行代码,编译器会认为是一个函数的声明
	//不会认为在创建对象
	//Person p1(); 类似于void func();
	
	
	/*cout << "p2的年龄为:" << p2.age << endl;
	cout << "p3的年龄为:" << p3.age << endl;*/
	
	//2.显示法
	//Person p1;
	//Person p2 = Person(10);//有参构造
	//Person p3 = Person(p2);//拷贝构造
	//Person(10); //匿名对象  特点:当前行执行结束后,系统会立即回收匿名对象
	
	//注意事项2
	//不要利用拷贝函数,初始化匿名对象,编译器会认为Person(p3)===Person p3,
	//会认为是对象的声明
	//Person(p3);
	
	//隐式转换法
	Person p4 = 10; //相当于写了Person p4= Person(10);
	Person p5 = p4; // 拷贝构造
}
int main()
{
    
    
	test01();
	system("pause");
	return 0;
}
  1. 拷贝构造函数调用时机

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

class Person
{
    
    
public:
	Person()
	{
    
    
		cout << "Person构造函数的调用" << endl;
	}

	Person(int age)
	{
    
    
		m_Age = age;
		cout << "Person有参构造函数的调用" << endl;
	}

	Person(const Person &p)
	{
    
    
		m_Age = p.m_Age;
		cout << "Person拷贝构造函数的调用" << endl;
	}

	~Person()
	{
    
    
		cout << "Person析构函数调用" << endl;
	}

	int m_Age;
};

// 1.使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
    
    
	Person p1(20);
	Person p2(p1);
	cout << "P2的年龄为:" << p2.m_Age << endl;
}

//2.值传递的方式给函数参数传递
void doWork(Person p)
{
    
    

}
void test02()
{
    
    
	Person p;
	doWork(p);
}
//3.以值方式返回局部对象
Person doWork2()
{
    
    
	Person p1;
	cout << (int*)&p1 << endl;
	return p1;
}
void test03()
{
    
    
	Person p = doWork2();
	cout << (int*)&p << endl;
}
int main()
{
    
    
	//test01()
	//test02();
	test03();
	system("pause");
	return 0;
}
  1. 构造函数调用案例

    默认情况下,c++编译器至少给一个类添加3个函数
      1.默认构造函数(无参,函数体为空)
      2.默认析构函数(无参,函数体为空)
      3.默认拷贝构造函数,对属性进行值拷贝
      
      拷贝函数调用规则如下:
          如果用户定义有参构造函数,c++不在提供默认无参构造,
          但是会提供默认拷贝函数
    	  如果用户定义拷贝构造函数,c++不在提供其他构造函数
    
#include <iostream>
using namespace std;
/*默认情况下,c++编译器至少给一个类添加3个函数
  1.默认构造函数(无参,函数体为空)
  2.默认析构函数(无参,函数体为空)
  3.默认拷贝构造函数,对属性进行值拷贝
  
  拷贝函数调用规则如下:
      如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝函数
	  如果用户定义拷贝构造函数,c++不在提供其他构造函数*/
class Person
{
    
    
public:
	/*Person()
	{
		cout << "Person的默认构造函数的调用" << endl;
	}
*/
	/*Person(int age)
	{
		cout << "Person有参构造函数的调用" << endl;
		m_Age = age;
	}*/
	~Person()
	{
    
    
		cout << "Person析构函数调用" << endl;
	}

	Person(const Person &p)
	{
    
    
		m_Age = p.m_Age;
		cout << "Person拷贝构造函数的调用" << endl;
	}

	int m_Age;
};

//void test01()
//{
    
    
//	Person p;
//	p.m_Age = 18;
//
//	Person p2(p);
//
//	cout << "p2的年龄为:" <<p2.m_Age<< endl;
//}

void test02()
{
    
    
	//Person p;
	Person p(28);
	Person p2(p);
	cout << "p2的年龄为:" << p2.m_Age << endl;
}
int main()
{
    
    
	//test01();
	test02();
	system("pause");
	return 0;
}

5.深拷贝浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
#include <iostream>
using namespace std;

/*深浅拷贝是面试经典问题,也是常见的一个坑

  浅拷贝:简单的赋值拷贝操作
  深拷贝:在堆区重新申请空间,进行拷贝操作*/
class Person
{
    
    
public:
	Person()
	{
    
    
		cout << "Person的默认构造函数的调用" << endl;
	}
	Person(int age,int height)
	{
    
    
		m_Height = new int(height);
		cout << "Person有参构造函数的调用" << endl;
		m_Age = age;
	}

	//自己实现一个拷贝构造函数,解决浅拷贝带来的问题
	//如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
	{
    
    
		cout << "Person拷贝构造函数的调用" << endl;
		m_Age = p.m_Age;
		// m_Height = p.m_Height;  编译器默认实现就是这行代码
		
		//深拷贝操作
		m_Height = new int(*p.m_Height);
	}
	~Person()
	{
    
    
		//析构代码,将堆区开辟的数据做释放的操作
		if (m_Height != NULL)
		{
    
    
			delete m_Height;
			m_Height = NULL;
		}
		cout << "Person析构函数调用" << endl;
	}
	int m_Age;
	int *m_Height;
	
};
void test01()
{
    
    
	Person p1(18,160);
	cout << "p1的年龄为:" << p1.m_Age <<"身高为:"<<*p1.m_Height<< endl;
	Person p2(p1);
	cout << "p2的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
}
int main()
{
    
    
	test01();
	system("pause");
	return 0;
}

6. 初始化列表

初始化列表
作用:C++提供了初始化列表语法,用来初始化属性

语法:构造函数(): 属性1(值1),属性2(值2)...{}
#include <iostream>
using namespace std;

/*初始化列表
  作用:C++提供了初始化列表语法,用来初始化属性
  
  语法:构造函数(): 属性1(值1),属性2(值2)...{}*/
class Person
{
    
    
public:
	
	//传统初始化操作
	/*Person(int a, int b, int c)
	{
		m_A = a;
		m_B = b;
		m_C = c;
	}*/

	//初始化列表初始化属性
	/*Person() :m_A(10), m_B(20), m_C(30)
	{

	}*/

	Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c)
	{
    
    

	}
	int m_A;
	int m_B;
	int m_C;
};

void test01()
{
    
    
	//Person p(10, 20, 30);
	//Person p;
	Person p(30, 20, 10);
	cout << "m_A=" << p.m_A << endl;
	cout << "m_B=" << p.m_B << endl;
	cout << "m_C=" << p.m_C << endl;
}
int main()
{
    
    
	test01();
	system("pause");
	return 0;
}

7. 类对象作为类成员

c++类中的成员可以是另一个类的对象,我们称该成员为对象成员*/
class A{};
class B
{
	A a;
};
B类中有对象A作为成员,A为对象成员
那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?
答:当其他类的对象作为本类成员,构造时候先构造类对象,再构造自身
	析构顺序与构造相反
#include <iostream>
using namespace std;
#include <string>
/*c++类中的成员可以是另一个类的对象,我们称该成员为对象成员*/
//class A{};
//class B
//{
    
    
//	A a;
//};
/*B类中有对象A作为成员,A为对象成员
那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?*/
class Phone
{
    
    
public:

	Phone(string pName)
	{
    
    
		cout << "Phone的构造函数调用" << endl;
		m_PName = pName;
	}
	~Phone()
	{
    
    
		cout << "Phone析构函数调用" << endl;
	}

	//手机品牌名称
	string m_PName;
};
class Person
{
    
    
public:
	//Phone m_Phone = pName;  隐式转换法
	Person(string name, string pName) :m_Name(name), m_Phone(pName)
	{
    
    
		cout << "Person的构造函数调用" << endl;
	}

	
	~Person()
	{
    
    
		cout << "Person析构函数调用" << endl;
	}
	//姓名
	string m_Name;
	//手机
	Phone m_Phone;
};

//当其他类的对象作为本类成员,构造时候先构造类对象,再构造自身
//析构顺序与构造相反
void test01()
{
    
    
	Person p("张三", "苹果MAX");

	cout << p.m_Name << "拿着:" << p.m_Phone.m_PName << endl;
}
int main()
{
    
    
	test01();
	system("pause");
	return 0;
}

8.静态对象

静态成员函数
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:
  	所有对象共享同一份数据
  	在编译阶段分配内存
  	类内声明,类外初始化

静态成员函数
  	所有对象共享同一个函数
  	静态成员函数只能访问静态成员变量
#include <iostream>
using namespace std;

/*静态成员函数
  静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
  
  静态成员分为:
      所有对象共享同一份数据
	  在编译阶段分配内存
	  类内声明,类外初始化
  
  静态成员函数
      所有对象共享同一个函数
	  静态成员函数只能访问静态成员变量*/
class Person
{
    
    
public:
	//静态成员函数
	static void func()
	{
    
    
		m_A = 100;//静态成员函数 可以访问   静态成员变量
		//m_B = 100;//静态成员函数 不可以访问 静态成员变量
		cout << "static void func调用" << endl;
	}

	static int m_A;//静态成员变量
	int m_B;//非静态成员函数

	//静态成员函数也是有访问权限的
private:
	static void func2()
	{
    
    
		cout << "static void func2调用" << endl;
	}
};

int Person::m_A = 0;
void test01()
{
    
    
	//通过对象访问
	Person p;
	p.func();
	//通过类名访问
	Person::func();
	//Person::func2(); 类外访问不到私有静态成员函数
}
int main()
{
    
    
	test01();
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45923342/article/details/105725050