C++ 构造函数与析构函数
构造器和析构器的作用
1.C++构造函数和析构函数主要负责构建对象和销毁对象,在构建对象的时候系统自己调用类中的构造函数, 在对象作用域结束后,调用析构函数销毁对象
2.构造函数用来完成事先的初始化和准备工作 (申请分配内存), 析构函数 用来完成事后所必须的清理工作 (清理内存)
3.构造器和析构器是程序员自己写出的 创建或销毁一个对象时所自动调用的方法
构造器(constructed)
系统在创建某个类的实例时会第一时间自动调用这个类的构造器
定义构造器的前提(面对对象编程技术开发程序的基本步骤)
定义一个有属性和方法的类(模板)
为该类创建一个成员变量(实现)
构造器是类中一种特殊的方法(成员函数)
构造器和通常方法的主要区别:
构造器的名字必须和它所在的类的名字一样
系统在创建某个类的实例时会第一时间自动调用这个类的构造器
构造器永远不会返回任何值
创建构造函数时,需要把构造函数声明放进类里面,结束类中声明后,则需要开始定义构造函数本身
声明一个构造器
class Car //构建一个类
{
Car(void);//声明Car类的构建函数
}
注意大小写与类名保持一致,在结束声明之后开始定义构造器本身:
Car::Car()//由于构造函数不返回任何值 也不带任何参数 因此此函数不必写成void Car::Car(void) 格式为 ClassName::ClassName()
{
color = "WHITE";
engine = "V8";
wheel = 4;
gas_tank = FULL_GAS;
}
注意:
每个类中必须和至少有一个构造器函数如果你未在类中定义构造器函数 那么编译器会使用如下语法帮你定义一个构造器Classname::ClassName(){}
这是一个没有代码内容的空构造器,除此之外,编译器还会替你创建一个副本构造器CopyConstructor
且此构造器的状态为不可见
构造对象数组:
之前说过,数组可以是任何一种数据类型,这当然也包括对象
如:Car mycars[10]
,其调用方法依旧是:mycar[x].running
其中x
表示指定对象数组元素的下标
来自 操作符重载项目 ↓
以在构造器形参列表后方加上:号的方式进行类成员的初始化
现在有一个类为Comlex
类 此类的构造器有两个传入参数[r,i]
,此类有两个double类成员[real,imag]
Comlex类构造器
Comlex(double r=0, double i=0) :real(r), imag(i) {};
为什么在构造器形参列表后面加上冒号:
?
其实冒号后的内容:real(r), imag(i)
是初始化值,意为传入的形参数据用来初始化类成员,相当于:
Comlex(double r=0, double i=0) //构造器形参列表
{
real = r;
imag=i;
}
注意:
此时Comlex类的构造器参数列表有两个参数,也就是说在建立对象时,需要在对象名后方跟上参数列表并输入两个参数,例如:
int main()
{Comlex a(3.0,5.0);}
当主函数中建立Comlex对象实例时,未给该对象添加参数列表时,例如:
int main(){Comlex a;}
则需要保证类的构造器在声明时构造器的形参列表中的形参一定是被初始化的(有默认值)
Comlex(double r=0, double i=0)
来自 操作符重载项目 ↑
析构器(finalizer)
析构器:对象消亡时,自动被调用,用来释放对象占用的空间
从前面的内容我们了解到,在创建对象时,系统都会自动调用一个特殊的方法,即构造器
相应地,在销毁一个对象时,系统也应该会调用另一个特殊方法达到效果。没错,这就是 析构器
构造器和析构器二者相辅相成,有许多共同之处。首先,析构器有着和构造器/类一样的名字,只不过名字前面多了一个波浪符~
的前缀
声明一个析构器
class Car
{
~Car();
}
其次,析构器也永远不带任何返回值
另外,析构器是不带任何参数的,所以析构器的声明永远是如下格式:
~ClassName()
注意:
1.一个类最多只有一个析构函数
2.不显式的定义析构函数系统会调用默认析构函数
3.栈中内存由系统自由分配和释放,使用new创建的指针对象是在堆中分配内存 当不需要该对象时 我们需要手动去释放内存,否则会导致内存泄漏
案例 使用类 构造函数 析构函数 编写一个打印名人名言的程序
#include <iostream>
#include<windows.h>
#include <fstream>
#include <string> //如果需要使用getline函数则必须包含此头文件
using namespace std;
class Quote //类名的第一个字母习惯上最好采用大写
{
public:
//建立一个类 :存放汽车数据 车漆色 引擎类型 油量 和 几个轮子
string quote,speaker;
ofstream fileOutPut;
Quote(void);
//todo在类中声明构造器
//构造器的命名必须和其所在的类的类名一致
//构造器不返回任何值 所以不用写void 析构器也不带任何参数
~Quote();
//todo在类中声明析构器
//析构器有着和构造器或类一样的名字,比构造器多个波浪符 " ~ " 前缀
//析构器不返回任何值 所以不用写void 析构器也不带任何参数 所以析构器声明永远是如下格式 ~ClassName();
void inputQuote();
//todo定义类中的普通成员函数(类中 声明 部分)
//声明一个写入名言的方法
void inputSpeaker();
//声明一个写入名人的方法
bool write();
//声明一个将输入的名人名言写入到指定文件的方法
};
Quote::Quote()
{
fileOutPut.open("Quote.txt", ios::app);
}
//todo在类外部对类中声明的构造器进行定义
//每个类至少要有一个构造器 如果在类中没有定义构造器 编译器会使用如下语法自动帮你定义一个构造器 ( Classname::ClassName(){} )除此之外 编译器还会替你创建一个副本构造器CopyConstructor
Quote::~Quote()
{
fileOutPut.close();
}
//todo对类中声明的析构器器进行定义
void Quote::inputQuote()
{
getline(cin,quote);
}
void Quote::inputSpeaker()
{
getline(cin, speaker);
}
bool Quote::write()
{
if (fileOutPut.is_open())
{
fileOutPut << quote << "|" << speaker << "\n";
}
else
{
return false;
}
}
//todo案例 名人名言写入程序
int main()
{
Quote writequote;
cout << "请输入一句名言:" << endl;
writequote.inputQuote();
cout << "请输入名言的作者:" << endl;
writequote.inputSpeaker();
if (writequote.write())//当write这个函数bool值为true时此条件成立
{
cout << "已成功写入文件 请打开查看" << endl;
}
else
{
cout <<"文件写入失败 请检查 " <<endl;
}
return 0;
}
效果
注:ios::app
在未检测到相关用户文件的情况下,会自动在源文件夹中建立一个同名文件