目录
1.类的6个默认成员函数
2. 构造函数
2.1 概念
2.2 特性
3.析构函数
3.1 概念
3.2 特性
4. 拷贝构造函数
4.1 概念
4.2 特征
class Date {}; //空类
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Init(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0;
}
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成 员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次
class Date
{
public:// 1.无参构造函数Date(){}// 2.带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
void TestDate()
{Date d1; // 调用无参构造函数Date d2(2015, 1, 1); // 调用带参的构造函数// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)Date d3();
}
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:
int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数
注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
class Date
{
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参 构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数
因为你不传参,我另一个这边有默认值,两个有一个就能完成工作,如下代码:全缺省的已经可以包含无参的功能,两个都在的话,创建对象可能出现问题
class Date
{
public://无参的构造函数Date(){_year = 1900;_month = 1;_day = 1;}//全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
析构函数是特殊的成员函数,其特征如下:
1. 析构函数名是在类名前加上字符 ~。 2. 无参数无返回值类型。 3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载 4. 对象生命周期结束时,C++编译系统系统自动调用析构函数以下为栈的实现
#include
using namespace std;
#include
#includetypedef int DataType;struct Stack
{
public:void Init(){_array = (DataType*)malloc(10 * sizeof(DataType));if (NULL == _array){assert(false);return;}//没上面问题容易给3,数量为0_capacity = 10;_size = 0;}//入栈void Push(DataType data){_array[_size] = data;++_size;}//出战void Pop(){if (Empty())return;_size--;}//获取栈顶元素DataType Top(){assert(!Empty());return _array[_size - 1];}bool Empty(){return 0 == _size;}int Size(){return _size;}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}private://扩容方法void _CheckCapacity(){}
private:DataType* _array;size_t _capacity;size_t _size;
};int main()
{Stack s;s.Init();s.Push(1);s.Push(2);s.Push(3);s.Push(4);s.Push(5);cout << s.Top() << endl;cout << s.Size() << endl;s.Pop();s.Pop();cout << s.Top() << endl;cout << s.Size() << endl;s.Destroy();return 0;
}
因为我们代码中自己写了初始化 Init 和 Destory,所以在主函数中你要进行栈操作,必须手动加上s.Init();以及s.Destroy(); 要是不加就会崩溃,这样的话你总有失误的时候,此时我们想让栈创建好就有空间,所以对代码需要优化,下面为部分优化代码
~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
void TestStack()
{Stack s;s.Push(1);s.Push(2);s.Push(3);s.Push(4);s.Push(66);cout << s.Top() << endl;cout << s.Size() << endl;s.Pop();s.Pop();cout << s.Top() << endl;cout << s.Size() << endl;
}
int main()
{TestStack();return 0;
}
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
注意:像Date类一样,对象中没有涉及到任何资源管理时,该类的析构函数可以不必给出
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};
void TestDate()
{Date d(2022, 11, 22);
}
int main()
{void TestDate();return 0;
}
Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date d2(d1);
错误例子如下:如果这样使用,会不断的调用拷贝,这就是传值引发对象的拷贝
Date(const Date& d)

问题:编译器生成的拷贝构造,虽然没有生成但是可以完成拷贝构造的工作,既然编译器已经可以完成了,那拷贝构造函数还需要用户自己写吗?、
答:像日期这种没有涉及到资源管理时, 可写可不写,一般不写,因为编译器可以完成拷贝的工作,如果需要自己再去实现,注意编译器是按照值的方式拷贝的---即:将一个对象中的内容原封不动的拷贝到另一个对象中(浅拷贝)
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构 造函数是一定要写的,否则就是浅拷贝注意:
// 1. 以值的方式返回时,如果返回的是匿名对象,则编译器不会再用匿名对象
// 拷贝构造临时对象,而是直接将匿名对象返回了
// 匿名对象:没有名字的对象
// 2. 如果参数是以值的方式传递,实参如果也是匿名对象,也会少一次拷贝构造
// 3. 在C++中,参数能使用引用尽量使用引用,如果不想通过形参改变外部的实参,尽量设置为const类型的引用
上一篇:C语言文件操作
下一篇:Java基础类型和运算符