主要学习内容
一、C++析构函数
- 析构函数的特征
(1)没有返回值
(2)函数名是在类名前面加~符号。
(3)和构造函数一样,也是一种特殊的成员函数,不需要程序员显示调用。
(4)析构函数没有参数,不能被重载,因此一个类只能有一个析构函数;如果用户没有定义,编译器会自动生成一个默认的析构函数。
注意:
a.析构函数一般是在销毁对象时自动调用的,比如释放分配的内存、关闭打开的文件。
b. 析构函数唯一的作用就是给删除的对象后释放已经分配的内存。
#include <iostream>
using namespace std;
//定义一个模拟变长数组的类
class Demo{
//成员函数
public:
//一般最先写构造函数
Demo(int len);
~Demo(); //析构函数,注意函数名为~类名,无形参无返回值
void input(); //从控制台获取数组元素
void show(); //打印数组
private:
//成员变量
const int m_len; //数组的长度,用const修饰,表明数组的长度m_len不可修改,只能用初始化列表进行赋值。
int *m_arr; //数组指针
int *m_p; //指向数组中的第i个元素的指针
int* parri(int i); //获取第i个元素的指针,返回值是一个指针
}; //类定义结束
//类的成员函数定义
//m_len使用了const修饰,只能使用初始化列表给构造函数形参赋值
Demo::Demo(int len):m_len(len){
//在堆中给数组分配内存
if(len > 0)
{
m_arr = new int[len];
}
else
{
m_arr = NULL;
}
}
//通过new分配的内存,一般在不使用后,需要释放内存,可以通过析构函数
Demo::~Demo(){
delete[] m_arr; //释放内存
}
int* Demo::parri(int i){
if(!m_arr || i < 0 || i >= m_len)
{
return NULL;
}
else
{
return m_arr + i;
}
}
//获取控制台的输入作为数组的元素
void Demo::input(){
for(int i = 0; m_p = parri(i); i++)
{
cin>>*parri(i);
}
}
void Demo::show(){
for(int i = 0; m_p = parri(i); i++)
{
if(i == m_len -1)
{
cout<<*parri(i)<<endl;
}
else
{
cout<<*parri(i)<<",";
}
}
}
//主函数
int main()
{
//输入一个含有n个元素的数组
int n;
cout<<"请输入数组的长度:";
cin>>n;
//调用构造函数在堆上创建对象
Demo *parr = new Demo(n);
//输入数组元素
cout<<"Input "<<"n"<<"number:";
parr->input();
//输出数组元素
cout<<"数组的元素有:";
parr->show();
//删除对象,也就是该数组
delete parr;
return 0;
}
输出结果:
注意:
a.在函数内部创建的对象是局部对象,它和局部变量类似,位于栈区,函数执行结束时会调用这些对象的析构函数。
b. new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。
二、C++中的对象数组
对象数组:就是数组的每个元素都是一个对象。
既然是对象,那么数组中的每个元素都需要用构造函数进行初始化。具体如何调用哪个构造函数,看下面的示例:
#include <iostream>
using namespace std;
class Demo{
public:
//无参构造函数
Demo(){
cout<<"called func1"<<endl;
}
//带参数的构造函数2
Demo(int n){
cout<<"called func2"<<endl;
}
};
int main()
{
cout<<"step1"<<endl;
Demo arr[2]; //调用func1,原因是无参数
cout<<"step2"<<endl;
Demo arr2[2]={3,6}; //都是调用func2,两个对象元素,两个初始化的值。
cout<<"step3"<<endl;
Demo arr3[2] = {4};//第一个元素是调func2,参数为4;第二个元素调func1,无参数
cout<<"step4"<<endl;
Demo *arr4 = new Demo[2]; //无参数,调func1
delete[] arr4;
return 0;
}
输出结果:
三、C++中的成员对象
成员对象:一个类的成员变量如果是另一个类的对象,就称之为“成员对象”。
包含成员对象的类叫封闭类(enclosed class)。
#include <iostream>
using namespace std;
class Type{
public:
//构造函数
Type(int year, int page):m_year(year),m_page(page){};
//成员函数
void show() const;
private:
int m_year;
int m_page;
};
//Book类内部含有一个类,因此book是一个封闭类
class Book{
public:
//带参数的构造函数,其中要给成员对象初始化
Book(int price, int year, int page):m_price(price),m_info(year,page){
};
void show() const;
private:
int m_price;
Type m_info; //Type是一个类,所以m_info是一个成员对象
};
void Type::show() const{
cout<<"出版年份:"<<m_year<<endl;
cout<<"该数的总页数:"<<m_page<<endl;
}
void Book::show()const{
cout<<"价格:"<<this->m_price<<"$"<<endl;
this->m_info.show();
}
int main()
{
Book newbook(20,2006,344);
newbook.show();
return 0;
}
运行结果:
注意:
a. 生成封闭类对象的语句一定要让编译器能够弄明白其成员对象是如何初始化的,否则就会编译错误。
#include <iostream>
using namespace std;
class Paper{
public:
Paper(){
cout<<"调用顺序2"<<endl;
}
~Paper(){
cout<<"调用顺序5"<<endl;
}
};
class Book{
public:
Book(){
cout<<"调用顺序1"<<endl;
}
~Book(){
cout<<"调用顺序6"<<endl;
}
};
//Shop类内部含有2个类对象,因此Shop是一个封闭类
class Shop{
public:
Shop(){
cout<<"调用顺序3"<<endl;
}
~Shop(){
cout<<"调用顺序4"<<endl;
}
private:
Book newbook;
Paper newpaper;
};
int main()
{
//创建对象,并自动调用默认的无参的构造器
Shop newshop;
return 0;
}
运行结果:
注意:
a. 封闭类对象生成时,先执行所有成员对象的构造函数,然后才执行封闭类自己的构造函数。
b. 成员对象构造函数的执行次序和成员对象在类定义中的次序一致,与它们在构造函数初始化列表中出现的次序无关。
比如:上面的例子中Shop类中先后定义了Book和Paper类的对象,由定义决定先后调用哪个类的构造函数初始化。
c. 当封闭类对象消亡时,先执行封闭类(先Shop类)的析构函数,然后再执行成员对象(paper和newbook)的析构函数。