C++ 封装(2): 构造函数和析构函数
C++远征之封装篇(上) **** 笔记 方便自己查阅和复习,温故而知新。
接着 C++ 封装(1) ——类和对象 继续做笔记。
目录
6 对象的生死离别
对象是如何存储的?
下面举一个例子,定义一个Car类,然后实例化三个对象。
那么这三个对象分别是怎么存储的?如何初始化?
下面使用自己定义的初始化函数( init( ) )来初始化对象。
但是上面的初始化比较繁琐,每次定义一个函数都要调用初始化函数。
有没有一种,只需要初始化一次,并且可以根据条件自动初始化的呢?
这时,C++使用 构造函数 来解决这个问题。
6.1 构造函数
构造函数的规则和特点:
(1) 构造函数在独享实例化时被自动调用;
(2) 构造函数与类同名;
(3) 构造函数没有返回值;
(4) 构造函数可以有多个重载形式;
(5) 实例化对象时仅用到一个构造函数;
(6) 当用户没有定义构造函数时,编译器自动生成一个构造函数。
构造函数有无参构造函数,有参构造函数和重载构造函数。
(1) 无参构造函数 形式见下图:
(2) 有参构造函数 形式见下图:
(3) 重载构造函数 形式见下图:
代码示例
//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;
}
运行结果:
6.2 默认构造函数
在定义构造函数时,可以加入默认值,即默认构造函数, 如下图所示:
还有一种加入默认值的方式为初始化列表,构造函数初始化列表格式如下:
初始化列表特性:
(1) 初始化列表先于构造函数执行;
(2) 初始化列表只能用于构造函数;
(3) 初始化列表可以同时初始化多个数据成员。
那么问题来了,初始化列表有那么重要吗?
下面看一个例子:
如上图所示,初始化列表可以给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;
}
运行结果:
6.3 拷贝构造函数
思考:
对于下面的例子,为什么只打印出一行 Student 呢?
实例化对象的时候一定会调用构造函数吗?
原因是在复制对象时,C++会自动生成拷贝构造函数。
拷贝构造函数 形式如下:
拷贝构造函数特性:
(1) 如果没有自定义的拷贝构造函数时,则系统会自动生成一个默认的拷贝构造函数。
(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;
}
运行结果:
6.4 析构函数
析构函数不允许添加任何参数,不能重载。
思考:析构函数有存在的必要性吗?
析构函数特性:
(1) 如果没有自定义的析构函数,则系统自动生成;
(2) 析构函数在对象销毁时自动调用;
(3) 析构函数没有返回值,没有参数也不能重载。
代码示例
要求:
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;
}
运行结果:
总结
参考资料
[1] C++远征之封装篇(上) (注:图片均来自视频中PPT)