C++ 复习总结记录三
主要内容
1、类的六个默认成员函数
2、构造函数
3、析构函数
4、拷贝构造函数
5、赋值运算符重载
6、const 成员函数
7、取所在及 const 取所在操纵符重载
一 类的六个默认成员函数
如果一个类中什么成员都没有,简称为空类。空类中并不是什么都没有,编译器会自动生成六个默认成员函数(用户没有显式实现,编译器自动生成的成员函数称为默认成员函数)
六个默认成员函数
- //初始化和清理
- 构造函数,完成初始化
- 析构函数,完成清理
- //拷贝复制
- 拷贝构造函数,使用同类对象初始化创建对象
- 赋值重载,把一个对象赋值给另一个对象
- //取地址重载
- 普通取地址函数
- const 取地址函数
复制代码 二 构造函数
2.1 概念
构造函数是一个特殊的成员函数,名字与类名雷同,创建类范例对象时由编译器自动调用,以保证每个数据成员都有 一个符合的初始值,而且在对象整个生命周期内只调用一次
注意,构造函数固然名称叫构造,但是构造函数任务不是开空间创建对象,而是初始化对象,特征如下
1、函数名与类名雷同
2、无返回值
3、对象实例化时编译器自动调用其构造函数
4、构造函数可以重载
- 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); // 调用带参的构造函数
- }
复制代码 2.2 注意要点
注意 : 如果通过无参构造函数创建对象时,对象后面不消跟括号,否则就成了函数声明
- void TestDate()
- {
- //warning C4930: “Date d3(void)”: 未调用原型函数
- //以下代码的函数 : 声明了d3函数, 该函数无参, 返回一个日期类型对象
- Date d3();
- }
复制代码 类中没有显式定义构造函数,编译器会自动生成一个无参的默认构造函数,一旦显式定义编译器将不再生成
- class Date
- {
- public:
- // 如果用户显式定义了构造函数,编译器将不再生成
- /*
- Date(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 类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
- // 将 Date 类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
- // 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
- Date d1;
- return 0;
- }
复制代码 2.3 默认构造函数作用
C++ 把范例分成内置范例(根本范例,语言提供的数据范例,如 int / char … 等)和自定义范例( class / struct / union 等自定义的范例),默认构造函数会对自定范例成员调用它的默认构造函数
- class Time
- {
- public:
- Time()
- {
- cout << "Time()" << endl;
- _hour = 0;
- _minute = 0;
- _second = 0;
- }
-
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date
- {
- private:
- // 基本类型(内置类型)
- int _year;
- int _month;
- int _day;
- // 自定义类型
- Time _t;
- };
- int main()
- {
- Date d;
- return 0;
- }
复制代码 注意:C++11 中针对内置范例成员不初始化的缺陷打了补丁,内置范例成员变量在类中声明时可给默认值
- class Time
- {
- public:
- Time()
- {
- cout << "Time()" << endl;
- _hour = 0;
- _minute = 0;
- _second = 0;
- }
-
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date
- {
- private:
- // 基本类型(内置类型)
- int _year = 1970;
- int _month = 1;
- int _day = 1;
- // 自定义类型
- Time _t;
- };
- int main()
- {
- Date d;
- return 0;
- }
复制代码 2.4 默认构造函数
无参构造函数、全缺省构造函数、编译器默认生成的构造函数,都是默认构造函数,且默认构造函数只能有一个
- 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;
- };
- void Test()
- {
- Date d1; //Date包含多个构造函数, 无法通过编译
- }
复制代码 三 析构函数
3.1 概念
析构函数:与构造函数功能相反,完成对象中资源的清理工作
它不是完成对对象本身的销毁,局部对象销毁工作由编译器完成,对象在销毁时会自动调用析构函数
析构函数特征如下
1、析构函数名是在类名前加上字符 ~
2、无参数无返回值范例
3、一个类只能有一个析构函数,若未显式定义,系统会自动生成默认析构函数(注意:析构函数不能重载)
4、对象生命周期竣事时,编译器自动调用析构函数
- typedef int DataType;
- class Stack
- {
- public:
- Stack(size_t capacity = 3)
- {
- _array = (DataType*)malloc(sizeof(DataType) * capacity);
- if (NULL == _array)
- {
- perror("malloc申请空间失败!!!");
- return;
- }
- _capacity = capacity;
- _size = 0;
- }
-
- void Push(DataType data)
- {
- // CheckCapacity();
- _array[_size] = data;
- _size++;
- }
-
- // 其他方法...
- ~Stack()
- {
- if (_array)
- {
- free(_array);
- _array = NULL;
- _capacity = 0;
- _size = 0;
- }
- }
-
- private:
- DataType* _array;
- int _capacity;
- int _size;
- };
- void TestStack()
- {
- Stack s;
- s.Push(1);
- s.Push(2);
- }
复制代码 3.2 默认析构函数作用
默认析构函数,对自定范例成员调用它的析构函数
- class Time
- {
- public:
- ~Time()
- {
- cout << "~Time()" << endl;
- }
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date
- {
- private:
- // 基本类型(内置类型)
- int _year = 1970;
- int _month = 1;
- int _day = 1;
- // 自定义类型
- Time _t;
- };
- int main()
- {
- Date d;
- return 0;
- }
复制代码 注:类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数即可,好比 Date 类(成员均为内置范例);有资源申请时,析构函数必须写,否则会造成资源走漏,好比 Stack 类
3.3 训练
两个栈实现一个队列
使用 STL 库
- #include <stack>
- using namespace std;
- class MyQueue {
- public:
- MyQueue() {
- }
- void push(int x) {
- s1.push(x);
- }
- int pop() {
- int res = peek();
- s2.pop();
- return res;
- }
- int peek() {
- if (s2.empty()) //先判断 s2 是否为空, 若 s2 不为空直接输出 s2 栈顶元素
- {
- while (!s1.empty()) //s2 为空则将 s1 的内容入 s2 栈
- {
- s2.push(s1.top());
- s1.pop();
- }
- }
- return s2.top();
- }
- bool empty() {
- return s1.empty() && s2.empty();
- }
- private:
- stack<int> s1, s2;
- };
复制代码 提交截图
C 自实现栈
- #define MAX_LEN 10
- typedef int DataType;
- class MyStack
- {
- public:
- MyStack()
- : _array((DataType*)malloc(_capacity * sizeof(DataType)))
- {
- if (nullptr == _array)
- {
- perror("malloc申请空间失败");
- return;
- }
- }
- ~MyStack()
- {
- if (_array) {
- free(_array);
- _array = nullptr;
- }
-
- }
- void push(const DataType& x) {
- _array[_size] = x;
- ++_size;
- if (_size > _capacity)
- {
- _capacity *= 2;
- DataType* tmpPtr = (DataType*)malloc(sizeof(DataType) * _capacity);
- memcpy(tmpPtr, _array, sizeof(DataType) * _capacity / 2);
- free(_array);
- _array = tmpPtr;
- }
- }
- void pop() {
- --_size;
- }
- int top() {
- return _array[_size - 1];
- }
- bool empty() {
- return _size == 0;
- }
- private:
- int _size = 0;
- int _capacity = MAX_LEN;
- DataType* _array; //类型数组
- };
- class MyQueue {
- public:
- MyQueue() {
- }
- void push(int x) {
- s1.push(x);
- }
- int pop() {
- int res = peek();
- s2.pop();
- return res;
- }
- int peek() {
- if (s2.empty()) //先判断 s2 是否为空, 若 s2 不为空直接输出 s2 栈顶元素
- {
- while (!s1.empty()) //s2 为空则将 s1 的内容入 s2 栈
- {
- s2.push(s1.top());
- s1.pop();
- }
- }
- return s2.top();
- }
- bool empty() {
- return s1.empty() && s2.empty();
- }
- private:
- MyStack s1, s2;
- };
复制代码 提交截图

四 拷贝构造函数
4.1 概念
拷贝构造函数
单个形参,该形参是对本类范例对象的引用 ( 一样平常常用 const 修饰 ),用已存在的对象创建新对象
特征
1、拷贝构造函数是构造函数的一个重载
2、拷贝构造函数参数只有一个且必须是范例对象引用(传值方式编译器直接报错,会引发无穷递归调用)
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- // Date(const Date& d) // 正确写法
- Date(const Date& d) // 错误写法:编译报错,会引发无穷递归
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
-
- private:
- int _year;
- int _month;
- int _day;
- };
- int main()
- {
- Date d1;
- Date d2(d1);
- return 0;
- }
复制代码 4.2 默认拷贝构造函数
若未显式定义,编译器会生成默认的拷贝构造函数
注意:编译器生成的默认拷贝构造函数中,内置范例按照字节方式直接拷贝(浅拷贝或值拷贝),而自定义范例是调用其拷贝构造函数完成拷贝
- class Time
- {
- public:
- Time()
- {
- _hour = 1;
- _minute = 1;
- _second = 1;
- }
-
- Time(const Time& t)
- {
- _hour = t._hour;
- _minute = t._minute;
- _second = t._second;
- cout << "Time::Time(const Time&)" << endl;
- }
-
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date
- {
- private:
- // 基本类型(内置类型)
- int _year = 1970;
- int _month = 1;
- int _day = 1;
- // 自定义类型
- Time _t;
- };
- int main()
- {
- Date d1;
- // 用已经存在的 d1 拷贝构造 d2, 此处会调用 Date 类的拷贝构造函数
- // 但 Date 类并没有显式定义拷贝构造函数, 则编译器会给 Date 类生成一个默认的拷贝构造函数
- Date d2(d1);
- return 0;
- }
复制代码 注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数必须写,否则就是浅拷贝,如下例
- typedef int DataType;
- class Stack
- {
- public:
- Stack(size_t capacity = 10)
- {
- _array = (DataType*)malloc(capacity * sizeof(DataType));
- if (nullptr == _array)
- {
- perror("malloc申请空间失败");
- return;
- }
- _size = 0;
- _capacity = capacity;
- }
-
- void Push(const DataType& data)
- {
- // CheckCapacity();
- _array[_size] = data;
- _size++;
- }
-
- ~Stack()
- {
- if (_array)
- {
- free(_array);
- _array = nullptr;
- _capacity = 0;
- _size = 0;
- }
- }
-
- private:
- DataType *_array;
- size_t _size;
- size_t _capacity;
- };
- int main()
- {
- Stack s1;
- s1.Push(1);
- s1.Push(2);
- s1.Push(3);
- s1.Push(4);
- Stack s2(s1);
- return 0;
- }
复制代码 将会导致瓦解
4.3 典范调用场景
1、使用已存在对象创建新对象
2、函数参数范例为类范例对象
3、函数返回值范例为类范例对象
- class Date
- {
- public:
- Date(int year, int minute, int day)
- {
- cout << "Date(int,int,int):" << this << endl;
- }
-
- Date(const Date& d)
- {
- cout << "Date(const Date& d):" << this << endl;
- }
-
- ~Date()
- {
- cout << "~Date():" << this << endl;
- }
-
- private:
- int _year;
- int _month;
- int _day;
- };
- Date Test(Date d)
- {
- Date temp(d);
- return temp;
- }
- int main()
- {
- Date d1(2022,1,13);
- Test(d1);
- return 0;
- }
复制代码 为了提高步调效率,一样平常对象传参时,尽量使用引用范例,返回时根据实际场景,能用引用尽量使用引用(只要是不函数内部栈对象,均可使用引用)
五 赋值运算符重载
5.1 运算符重载
C++为增强代码可读性引入了运算符重载**,**运算符重载是具有特殊函数名的函数
函数名字为:关键字 operator 后面接需要重载的运算符符号
函数原型:返回值范例 operator操纵符 ( 参数列表 )
注意
- 不能通过连接其他符号来创建新的操纵符:好比operator@
- 重载操纵符必须有一个类范例参数
- 用于内置范例的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员函数重载时,其形参看起来比操纵数数目少一,因为成员函数的第一个参数为隐蔽的 this
- .* :: sizeof ?: . 以上5个运算符不能重载
- // 全局的 operator==
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- //private:
- int _year;
- int _month;
- int _day;
- };
- //全局运算符重载需要成员变量是公有的, 不能保证封装性, 如何解决 ?
- //可以用友元解决或重载成成员函数
- bool operator==(const Date& d1, const Date& d2)
- {
- return d1._year == d2._year
- && d1._month == d2._month
- && d1._day == d2._day;
- }
- void Test ()
- {
- Date d1(2018, 9, 26);
- Date d2(2018, 9, 27);
- cout << (d1 == d2) << endl;
- }
复制代码 如下
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- //bool operator == (Date* this, const Date& d2)
- //注意左操作数是 this, 指向调用函数的对象
- bool operator==(const Date& d2)
- {
- return _year == d2._year;
- && _month == d2._month
- && _day == d2._day;
- }
-
- private:
- int _year;
- int _month;
- int _day;
- };
复制代码 5.2 赋值运算符重载
1、赋值运算符重载格式
参数范例:const T&,传递引用提高传参效率
返回值范例:T& 返回引用提高效率,有返回值为了支持连续赋值
检测是否本身给本身赋值
返回 *this 要复合连续赋值的含义
- class Date
- {
- public :
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- Date (const Date& d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
- Date& operator=(const Date& d)
- {
- if(this != &d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
- return *this;
- }
-
- private:
- int _year ;
- int _month ;
- int _day ;
- };
复制代码 2、赋值运算符只能重载为类成员函数不能重载为全局函数
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- int _year;
- int _month;
- int _day;
- };
- // 赋值运算符重载为全局函数, 注意没有 this 指针, 需要给两个参数
- Date& operator=(Date& left, const Date& right)
- {
- if (&left != &right)
- {
- left._year = right._year;
- left._month = right._month;
- left._day = right._day;
- }
- return left;
- }
- // 编译失败:
- // error C2801: “operator =”必须是非静态成员
复制代码 原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时再在类外实现一个全局赋值运算符重载,和编译器生成的默认赋值运算符重载冲突,故赋值运算符重载只能是类的成员函数
C++ PRIME 第五版 500 页
3、默认赋值运算符重载
没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置范例成员变量是直接赋值的,而自定义范例成员变量需要调用对应类的赋值运算符重载完成赋值(和类似拷贝构造)
5.3 前置++ 和后置++ 重载
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- // 前置++: 返回 +1 之后的结果
- // 注意: this 指向的对象函数结束后不会销毁, 以引用方式返回提高效率
- Date& operator++()
- {
- _day += 1;
- return *this;
- }
-
- // 后置++
- // 前置++ 和后置++ 都是一元运算符, 为了让前置++ 与后置++ 形成能正确重载, C++ 规定后置++ 重载时多增加一个 int 类型参数, 但调用函数时该参数不用传递,编译器自动传递
- // 注意: 后置++ 是先使用后 +1, 因此需要返回 +1 之前的旧值, 故需在实现时需要先将 this 保存一份, 然后给 this + 1. 而 temp 是临时对象, 因此只能以值的方式返回, 不能返回引用
- Date operator++(int)
- {
- Date temp(*this);
- _day += 1;
- return temp;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
- int main()
- {
- Date d;
- Date d1(2022, 1, 13);
- d = d1++; // d: 2022,1,13 d1:2022,1,14
- d = ++d1; // d: 2022,1,15 d1:2022,1,15
- return 0;
- }
复制代码 六 const 成员
将 const 修饰的成员函数称之为 const 成员函数,const 修饰类成员函数,实际修饰该成员函数隐含的 this 指针,表明在该成员函数中不能对类的任何成员举行修改
针对以下代码
- class Date
- {
- public:
- Date(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
-
- void Print()
- {
- cout << "Print()" << endl;
- cout << "year:" << _year << endl;
- cout << "month:" << _month << endl;
- cout << "day:" << _day << endl;
- }
-
- void Print() const
- {
- cout << "Print() const" << endl;
- cout << "year:" << _year << endl;
- cout << "month:" << _month << endl;
- cout << "day:" << _day << endl;
- }
-
- private:
- int _year; // 年
- int _month; // 月
- int _day; // 日
- };
- void Test()
- {
- Date d1(2022,1,13);
- d1.Print();
- const Date d2(2022,1,13);
- d2.Print();
- }
复制代码 回答下列问题
1、const 对象可以调用非 const 成员函数吗
不可以,因为 const 对象的成员变量无法修改,this 指针的范例为 const type*
2、非 const 对象可以调用 const 成员函数吗
可以
3、const 成员函数内可以调用其它的非 const 成员函数吗
不可以
4、非 const 成员函数内可以调用其它的 const 成员函数吗
可以, const 成员函数不会修改成员变量,非 const 成员函数可能会修改,属于权限放大
七 取所在及 const 取所在操纵符重载
这两个默认成员函数一样平常不消重新定义 ,编译器默认会生成
- class Date
- {
- public :
- Date* operator&()
- {
- return this;
- }
-
- const Date* operator&() const
- {
- return this;
- }
-
- private :
- int _year; // 年
- int _month; // 月
- int _day; // 日
- };
复制代码 这两个运算符一样平常不需要重载,使用编译器生成的默认取所在的重载即可。只有特殊情况,才需要重载,好比想让外部无法获取到该类对象所在时
- class Date
- {
- public :
- Date* operator&()
- {
- return nullptr;
- }
-
- const Date* operator&() const
- {
- return nullptr;
- }
-
- private :
- int _year; // 年
- int _month; // 月
- int _day; // 日
- };
复制代码 八 日期类的实现
给出 Date 类如下头文件,实现 Date.cpp
- class Date
- {
- public:
- // 获取某年某月的天数
- int GetMonthDay(int year, int month)
- {
- static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- int day = days[month];
- if (month == 2
- && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
- {
- day += 1;
- }
- return day;
- }
- // 全缺省的构造函数
- Date(int year = 1900, int month = 1, int day = 1);
- // 拷贝构造函数
- // d2(d1)
- Date(const Date& d);
- // 赋值运算符重载
- // d2 = d3 -> d2.operator=(&d2, d3)
- const Date& operator=(const Date& d);
- // 析构函数
- ~Date();
- // 日期+=天数
- Date& operator+=(int day);
- // 日期+天数
- Date operator+(int day) const;
- // 日期-天数
- Date operator-(int day) const;
- // 日期-=天数
- Date& operator-=(int day);
- // 前置++
- Date& operator++();
- // 后置++
- Date operator++(int);
- // 后置--
- Date operator--(int);
- // 前置--
- Date& operator--();
- // >运算符重载
- bool operator>(const Date& d) const;
- // ==运算符重载
- bool operator==(const Date& d) const;
- // >=运算符重载
- bool operator >= (const Date& d) const;
- // <运算符重载
- bool operator < (const Date& d) const;
- // <=运算符重载
- bool operator <= (const Date& d) const;
- // !=运算符重载
- bool operator != (const Date& d) const;
- // 日期-日期 返回天数
- int operator-(const Date& d) const;
- private:
- int _year;
- int _month;
- int _day;
- };
复制代码 代码实现
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |