C++ 封装(2): 构造函数和析构函数

C++远征之封装篇(上) **** 笔记 方便自己查阅和复习,温故而知新。

接着 C++ 封装(1) ——类和对象 继续做笔记。

目录

6 对象的生死离别

6.1 构造函数

代码示例

6.2 默认构造函数

代码示例

6.3 拷贝构造函数

构造函数总结

代码示例

6.4 析构函数

代码示例

总结

参考资料


6 对象的生死离别

对象是如何存储的?

C++ 封装(2): 构造函数和析构函数

下面举一个例子,定义一个Car类,然后实例化三个对象。

C++ 封装(2): 构造函数和析构函数

那么这三个对象分别是怎么存储的?如何初始化?

C++ 封装(2): 构造函数和析构函数

下面使用自己定义的初始化函数( init( ) )来初始化对象。

C++ 封装(2): 构造函数和析构函数

但是上面的初始化比较繁琐,每次定义一个函数都要调用初始化函数。

有没有一种,只需要初始化一次,并且可以根据条件自动初始化的呢?

C++ 封装(2): 构造函数和析构函数

这时,C++使用 构造函数 来解决这个问题。

 

6.1 构造函数

构造函数的规则和特点:

(1) 构造函数在独享实例化时被自动调用;

(2) 构造函数与类同名;

(3) 构造函数没有返回值;

(4) 构造函数可以有多个重载形式;

(5) 实例化对象时仅用到一个构造函数;

(6) 当用户没有定义构造函数时,编译器自动生成一个构造函数。

 

构造函数有无参构造函数有参构造函数重载构造函数

(1) 无参构造函数 形式见下图:

C++ 封装(2): 构造函数和析构函数

 

(2) 有参构造函数 形式见下图:

C++ 封装(2): 构造函数和析构函数

 

(3) 重载构造函数 形式见下图:

C++ 封装(2): 构造函数和析构函数

 

代码示例

//Teacher.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Teacher
{
public:
	Teacher();//无参构造函数声明
	Teacher(string name, int age);//有参构造函数声明

	void setName(string name);
	string getName();
	void setAge(int age);
	int getAge();

private:
	string m_strName;
	int m_iAge;
};

 

//Teacher.cpp

#include"Teacher.h"
using namespace std;
Teacher::Teacher()
{
	m_strName = "SB";
	m_iAge = 3;
	cout << "Teacher() 无参" << endl;
}
Teacher::Teacher(string name, int age)
{
	m_strName = name;
	m_iAge = age;
	cout << "Teacher(string name, int age) 有参" << endl;
}

//函数定义
void Teacher::setName(string name)
{
	m_strName = name;
}

string Teacher::getName()
{
	return m_strName;
}

void Teacher::setAge(int age)
{
	 m_iAge=age;
}

int Teacher::getAge()
{
	return m_iAge;
}
//main.cpp
#include<iostream>
#include<string>
#include"Teacher.h"
using namespace std;


int main()
{
	//调用构造函数
	Teacher t1;//调用无参构造函数
	cout << "Name " << t1.getName() <<endl;
	cout << "Age " << t1.getAge() << endl;

	cout << "----------------------------------" << endl;
	Teacher t2("2B",18);//调用有参构造函数
	cout << "Name " << t2.getName() << endl;
	cout << "Age " << t2.getAge() << endl;

	cin.get();
	return 0;
}

运行结果:

C++ 封装(2): 构造函数和析构函数

 

 

6.2 默认构造函数

在定义构造函数时,可以加入默认值,即默认构造函数, 如下图所示:

C++ 封装(2): 构造函数和析构函数

 

还有一种加入默认值的方式为初始化列表,构造函数初始化列表格式如下:

C++ 封装(2): 构造函数和析构函数

 

初始化列表特性:

(1) 初始化列表先于构造函数执行;

(2) 初始化列表只能用于构造函数;

(3) 初始化列表可以同时初始化多个数据成员。

 

那么问题来了,初始化列表有那么重要吗?

C++ 封装(2): 构造函数和析构函数

 

下面看一个例子:

C++ 封装(2): 构造函数和析构函数

 

C++ 封装(2): 构造函数和析构函数

如上图所示,初始化列表可以const常量赋值,这个是构造函数无法做到的。

 

代码示例

要求:

Teacher 类:
自定义有默认参数的构造函数;
使用初始化列表 对数据进行初始化;
数据:名字 年龄;
成员函数:数据成员的封装函数;
拓展:定义可以最多学生的个数,此为常量

//Teacher.h

#pragma once

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

class Teacher
{
public:
	//构造函数声明  默认构造函数
	Teacher(string name = "SB", int age = 1, int n = 100);

	//函数声明
	void setName(string name);
	string getName();
	void setAge(int age);
	int getAge();
	int getMax();

private:
	string m_strName;
	int m_iAge;
	const int m_iMax;//常量
};

 

//Teacher.cpp

#include"Teacher.h"
using namespace std;
//使用初始化列表
Teacher::Teacher(string name, int age, int m) :m_strName(name), m_iAge(age), m_iMax(m)//常量 必须使用初始化列表
{
	cout << "Teacher(string name, int age, int m)" << endl;
}

//函数定义
void Teacher::setName(string name)
{
	m_strName = name;
}

string Teacher::getName()
{
	return m_strName;
}

void  Teacher::setAge(int age)
{
	m_iAge = age;
}

int  Teacher::getAge()
{
	return m_iAge;
}

int  Teacher::getMax()
{
	return m_iMax;
}

 

//main.cpp

#include<iostream>
#include<string>
#include"Teacher.h"
using namespace std;

/**
Teacher 类:
自定义有默认参数的构造函数
使用初始化列表 对数据进行初始化
数据:名字 年龄
成员函数:数据成员的封装函数
拓展:定义可以最多学生的个数,此为常量
*/

int main()
{
	Teacher t1;
	cout << t1.getName() << " " << t1.getAge() << " " << t1.getMax() << endl;

	cout << "-------------------------------------" << endl;
	Teacher t2("2B", 18, 250);
	cout << t2.getName() << " " << t2.getAge() <<" "<<t2.getMax()<< endl;


	cin.get();
	return 0;
}

运行结果:

C++ 封装(2): 构造函数和析构函数

 

 

6.3 拷贝构造函数

思考:

对于下面的例子,为什么只打印出一行 Student 呢?

实例化对象的时候一定会调用构造函数吗?

C++ 封装(2): 构造函数和析构函数

 

C++ 封装(2): 构造函数和析构函数

 

原因是在复制对象时,C++会自动生成拷贝构造函数。

拷贝构造函数 形式如下:

C++ 封装(2): 构造函数和析构函数

 

拷贝构造函数特性:

(1) 如果没有自定义的拷贝构造函数时,则系统会自动生成一个默认的拷贝构造函数。

(2) 当采用直接初始化或复制初始化实例化对象时,系统会自动调用拷贝构造函数。

 

构造函数总结

C++ 封装(2): 构造函数和析构函数

C++ 封装(2): 构造函数和析构函数

 

 

代码示例

要求:

定义 Teacher 类:自定义拷贝函数
数据成员:名字  年龄
成员函数:数据成员的封装函数

//Teacher.h

#pragma once

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

class Teacher
{
public:
	//构造函数声明  
	Teacher(string name = "SB", int age = 1);
	Teacher(const Teacher &tea);//拷贝构造函数声明


	//函数声明
	void setName(string name);
	string getName();
	void setAge(int age);
	int getAge();
	int getMax();

private:
	string m_strName;
	int m_iAge;
};

 

//Teacher.cpp

#include"Teacher.h"
using namespace std;
//使用初始化列表
Teacher::Teacher(string name, int age) :m_strName(name), m_iAge(age)
{
	cout << "Teacher(string name, int age)" << endl;
}

//拷贝构造函数定义
Teacher::Teacher(const Teacher &tea)
{
	cout << "Teacher(const Teacher &tea) 拷贝" << endl;
}


//函数定义
void Teacher::setName(string name)
{
	m_strName = name;
}

string Teacher::getName()
{
	return m_strName;
}

void  Teacher::setAge(int age)
{
	m_iAge = age;
}

int  Teacher::getAge()
{
	return m_iAge;
}

 

//Teacher.cpp

#include"Teacher.h"
using namespace std;
//使用初始化列表
Teacher::Teacher(string name, int age) :m_strName(name), m_iAge(age)
{
	cout << "Teacher(string name, int age)" << endl;
}

//拷贝构造函数定义
Teacher::Teacher(const Teacher &tea)
{
	cout << "Teacher(const Teacher &tea) 拷贝" << endl;
}


//函数定义
void Teacher::setName(string name)
{
	m_strName = name;
}

string Teacher::getName()
{
	return m_strName;
}

void  Teacher::setAge(int age)
{
	m_iAge = age;
}

int  Teacher::getAge()
{
	return m_iAge;
}

运行结果:

C++ 封装(2): 构造函数和析构函数

 

 

6.4 析构函数

 

C++ 封装(2): 构造函数和析构函数

析构函数不允许添加任何参数,不能重载。

思考:析构函数有存在的必要性吗?

C++ 封装(2): 构造函数和析构函数

 

析构函数特性:

(1) 如果没有自定义的析构函数,则系统自动生成;

(2) 析构函数在对象销毁时自动调用;

(3) 析构函数没有返回值,没有参数也不能重载。

 

C++ 封装(2): 构造函数和析构函数

 

 

代码示例

要求:

Teacher 类:
1 自定义拷贝函数 和析构函数
2 普通方式实例化对象,在销毁时是否自动调用析构函数? 是
3 通过拷贝函数实例化对象,在销毁对象时是否自动调用析构函数? 是
数据成员:名字  年龄
成员函数:数据成员的封装函数

//Teacher.h

#pragma once

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

class Teacher
{
public:
	//构造函数声明  
	Teacher(string name = "SB", int age = 1);
	Teacher(const Teacher &tea);//拷贝构造函数声明

	~Teacher();//析构函数 声明

	//函数声明
	void setName(string name);
	string getName();
	void setAge(int age);
	int getAge();
	int getMax();

private:
	string m_strName;
	int m_iAge;
};

 

//Teacher.cpp

#include"Teacher.h"
using namespace std;
//使用初始化列表
Teacher::Teacher(string name, int age) :m_strName(name), m_iAge(age)
{
	cout << "Teacher(string name, int age)" << endl;
}

//拷贝构造函数定义
Teacher::Teacher(const Teacher &tea)
{
	cout << "Teacher(const Teacher &tea) 拷贝" << endl;
}

//析构函数 定义
Teacher::~Teacher()
{
	cout<<"~Teacher() 析构"<<endl;
}


//函数定义
void Teacher::setName(string name)
{
	m_strName = name;
}

string Teacher::getName()
{
	return m_strName;
}

void  Teacher::setAge(int age)
{
	m_iAge = age;
}

int  Teacher::getAge()
{
	return m_iAge;
}

 

//main.cpp

#include<iostream>
#include<string>
#include"Teacher.h"
using namespace std;

/**
Teacher 类:
1 自定义拷贝函数 和析构函数
2 普通方式实例化对象,在销毁时是否自动调用析构函数? 是
3 通过拷贝函数实例化对象,在销毁对象时是否自动调用析构函数? 是
数据成员:名字  年龄
成员函数:数据成员的封装函数
*/

void test(Teacher t)
{

}

int main()
{
	Teacher t1;

	cout << "----------------------------------" << endl;

	Teacher *p = new Teacher;
	delete p;

	cout<<"----------------------------------"<<endl;

	Teacher t2(t1);// 值传递 调用 会触发拷贝构造函数

	cin.get();
	return 0;
}

运行结果:

C++ 封装(2): 构造函数和析构函数

 

 

总结

C++ 封装(2): 构造函数和析构函数

 

C++ 封装(2): 构造函数和析构函数

 

C++ 封装(2): 构造函数和析构函数

 

参考资料

[1]  C++远征之封装篇(上) (注:图片均来自视频中PPT)