C++ 总结 基本成员函数
欢迎访问我的博客首页。
基本成员函数
- 1. 无参构造函数
- 2. 有参构造函数
- 3. 拷贝构造函数
- 3.1 非 explict
- 3.2 合成的拷贝构造函数是删除的
- 3.3 合成的拷贝构造函数怎么拷贝
- 4. 移动构造函数
- 4.1 合成的移动构造函数
- 4.2 std::move 匹配拷贝构造函数
- 4.2 支持移动构造
- 5. 拷贝赋值运算符重载函数
- 6. 移动赋值运算符重载函数
- 7. 输入输出运算符重载函数
- 8. 取地址操作符重载函数
- 9. 析构函数
- 10. 合成的成员函数
类的构造函数和赋值运算符重载函数是常用且重要的两大类成员函数, 前者用于控制对象的初始化,后者用于控制对象的拷贝。本节介绍 11 个成员函数,包括 4 种构造函数、2 种赋值运算符重载函数、输入输出运算符重载函数、2 个取地址运算符函数、1 个析构函数。
1. 无参构造函数
无参构造函数。如果我们不定义任何构造函数,编译器将合成一个无参构造函数,称为合成的构造函数。合成的构造函数按如下规则初始化类的数据成员:
- 如果存在类内的初始值,用它来初始化成员。
- 否则,默认初始化该成员。
2. 有参构造函数
除了聚合类,如果我们想通过指定的参数初始化类,必须定义有参构造函数。可以使用直接初始化、列表初始化两种方式调用有参构造函数。
3. 拷贝构造函数
3.1 非 explict
如果一个类没有移动操作(没有合成的移动操作,也没有显示定义的移动操作),类会使用对应的拷贝操作来代替移动操作。这是因为 const 引用可以绑定右值,即 T&& 类型隐式转换为 const T& 类型:
// const 引用绑定右值。
const int &r = i * 42;
因此,explict 的拷贝构造函数无法代替移动构造函数。
3.2 合成的拷贝构造函数是删除的
如果我们没有定义拷贝构造函数,编译器会为我们合成一个拷贝构造函数。如果我们定义了移动构造函数或(和)移动赋值运算符重载函数,有些编译器合成的拷贝构造函数是删除的。
3.3 合成的拷贝构造函数怎么拷贝
合成的拷贝构造函数拷贝方法:
- 类类型的成员:使用其拷贝构造函数。
- 内置类型:直接拷贝。
- 数组:逐元素拷贝。如果元素是类类型,则使用其拷贝构造函数拷贝。
合成的拷贝操作只能进行浅拷贝,这很容易引起错误:
struct Example2 {Example2(int val) : val(val), ptr(new int(val)) {}~Example2() {cout << "析构开始。类地址:" << this << ",指针地址:" << &ptr << ",待释放内存地址:" << ptr << "。" << endl;delete ptr;ptr = nullptr;cout << "析构完成。类地址:" << this << ",指针地址:" << &ptr << ",待释放内存地址:" << ptr << "。" << endl;}int val, *ptr;
};int main() {Example2 ex1(1);Example2 ex2(ex1);
}
运行上面的代码会报错如下。对象 ex2 是我们调用合成的拷贝构造函数创建的。因为合成的拷贝构造函数进行浅拷贝,所以 ex1 和 ex2 的指针指向同一个地址 0x560f0f10ceb0。
同一作用域内,对象的析构顺序与创建顺序相反,即,先析构 ex2 再析构 ex1。ex2 的析构函数释放地址 0x560f0f10ceb0,ex1 的析构函数去释放地址 0x560f0f10ceb0 时就出现错误。
析构开始。类地址:0x7ffe407c1c70,指针地址:0x7ffe407c1c78,待释放内存地址:0x560f0f10ceb0。
析构完成。类地址:0x7ffe407c1c70,指针地址:0x7ffe407c1c78,待释放内存地址:0。
析构开始。类地址:0x7ffe407c1c60,指针地址:0x7ffe407c1c68,待释放内存地址:0x560f0f10ceb0。
free(): double free detected in tcache 2
已放弃 (核心已转储)
(base) jyy@pc:~/wor
4. 移动构造函数
4.1 合成的移动构造函数
以下 3 个条件都成立时,编译器才会为我们合成移动操作(移动构造函数和移动赋值运算符重载函数):
- 没有定义拷贝控制成员(拷贝构造函数、拷贝赋值运算符重载函数)。
- 没有定义析构函数。
- 类的每个 static 数据成员都可以移动。
可移动的数据成员:
- 内置类型。
- 有移动操作的类类型。这种类有合成的移动操作或显示地定义了移动操作。
下面的代码利用合成的移动构造函数实现移动构造。
struct Example2 {Example2(int val) : val(val), ptr(new int(val)) {}int val, *ptr;
};int main() {Example2 ex1(1);cout << &ex1 << endl; // 地址 1。cout << ex1.ptr << endl; // 地址 2。Example2 ex2 = std::move(ex1);cout << &ex2 << endl; // 地址 3。cout << ex2.ptr << endl; // 地址 2。
}
4.2 std::move 匹配拷贝构造函数
如果一个类没有移动操作(没有合成的移动构造函数和移动赋值运算符重载函数,也没有显示定义的移动构造函数和移动赋值运算符重载函数),类会使用对应的拷贝操作来代替移动操作。这是因为 const 引用可以绑定右值。
// const 引用绑定右值。
const int &r = i * 42;
左值引用通过 & 获得,右值引用通过 && 获得。因为 const 引用可以绑定右值,所以没有移动构造函数时,形参为 const 引用的拷贝构造函数会代替移动构造函数被 std::move 调用。
struct Example2 {Example2(int val) : val(val), ptr(new int(val)) {}Example2(const Example2& e) : val(e.val), ptr(nullptr) {cout << "拷贝构造函数(复制构造函数)。" << endl;if (e.ptr) {ptr = new int;memcpy(ptr, e.ptr, sizeof(int));}}~Example2() {delete ptr;ptr = nullptr;}int val, *ptr;
};int main() {Example2 ex1(1);cout << &ex1 << endl; // 地址 1。cout << ex1.ptr << endl; // 地址 2。Example2 ex2 = std::move(ex1);cout << &ex2 << endl; // 地址 3。cout << ex2.ptr << endl; // 地址 4。
}
4.2 支持移动构造
如果编译器不为我们合成移动操作,移动操作就会被拷贝操作代替。因此,如果没有合成的移动操作且我们不想让拷贝操作代替移动操作,就必须定义移动构造函数。
5. 拷贝赋值运算符重载函数
6. 移动赋值运算符重载函数
7. 输入输出运算符重载函数
8. 取地址操作符重载函数
9. 析构函数
10. 合成的成员函数
如果我们不声明自己的拷贝构造函数或拷贝赋值运算符,编译器总会为我们合成这些操作。编译器合成的这两个拷贝操作,要么被定义为逐成员拷贝,要么被定义为对象赋值,要么被定义为删除的函数。
C++ 总结 基本成员函数相关推荐
- C++ 笔记(16)— 类和对象(类定义、类实例对象定义、访问类成员、类成员函数、类 public/private/protected 成员、类对象引用和指针)
1. 类的定义 类定义是以关键字 class 开头,后跟类的名称.并在它后面依次包含类名,一组放在 {} 内的成员属性和成员函数,以及结尾的分号. 类声明将类本身及其属性告诉编译器.类声明本身并不能改 ...
- 成员变量和成员函数分开存储
首先来说结论: 1.在C++中,类内的成员变量和成员函数分开存储 2.只有非静态成员变量才属于类的对象上 空类(类中上面对象都没没有)占用内存大小为:1 #include <iostream&g ...
- c++成员函数的重载、覆盖、隐藏区别
c++成员函数的重载.覆盖.隐藏区别 成员函数的重载.覆盖(override)与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防. 重载与覆盖 成员函数被重载的特征: (1)相同的范围 ...
- 继承和多态 2.0 -- 继承的六个默认成员函数
本文重要介绍普通继承中如何写派生类的六个默认成员函数,主要是针对在派生类中,如何调用基类的六个默认成员函数 需要说明的一点就是,如果子类中没有调用父类的函数时,系统会自动生成一个. 构造函数 子类中有 ...
- C++自动生成的成员函数
每个类都有构造函数和析构函数.如果程序员没有编写这些成员函数,C++将自动生成他们. 1.class::class() 默认的构造函数. 如果没有定义其它的构造函数就会自动生成.生成的代码使用随机值填 ...
- 【C++】对象实例化/成员函数/成员变量的内存管理
文章目录 1. 对象实例化的内存管理 总结 2.C++成员函数在内存中的存储方式 3.C++类的实例化对象的大小之sizeof() 实例一: 实例二: 实例三: 实例四: 实例五: 实例六: 实例七: ...
- 《C++面向对象高效编程(第2版)》——3.11 类名、成员函数名、参数类型和文档...
本节书摘来自异步社区出版社<C++面向对象高效编程(第2版)>一书中的第3章,第3.11节,作者: [美]Kayshav Dattatri,更多章节内容可以访问云栖社区"异步社区 ...
- const常对象成员与常成员函数
#include <iostream> #include <string> using namespace std; class pt{ public: pt(int a,in ...
- C++成员变量指针和成员函数指针【The semantics of funcitons】
原文:https://blog.csdn.net/laojiu_/article/details/68946915 (原文有笔误) 1. #include <cstdio> #includ ...
- C++——运算符的重载---以成员函数方式重载---以友元函数方式重载
一.运算符的重载 1.运算符的重载 允许把标准运算符(如+ - * /等运算符)应用于自定义数据类型的对象,可以提高程序的可读性,运算符的重载本质上还是函数重载.运算符仅仅是语法上的方便,它是另一种函 ...
最新文章
- Android的px、dp和sp
- php 处理对象用什么,程序处理的对象是什么
- electron 打包_Vue3+Electron整合方式
- C++之namespace和::使用总结
- 使用rdbtools分析redis内存使用
- mysql数字_MySQL 中的数字类型
- 纯新手DSP编程--5.18--调试(续)
- 电子计算机的五个部分组成,电子计算机由哪几大部分构成?
- Python学习笔记之基础练习(一)
- Windows 服务(1) (from MSDN)
- mysql 章节作业题
- Springboot:整合DubboProvider的配置以及Comsumer的配置
- 【微信小程序】用户授权以及判断登录是否过期的方法
- 判断有向图中是否存在从vi到vj的路径
- 计算机专业课科目,考研计算机专业课有哪些科目
- 本地唯一985,要去省会了!
- Linux 命令之 7za -- 文件压缩命令
- 浅析代理IP与VPS
- 实现带头结点的单链表元素就地逆置
- DOS/WinPE双启动U盘Gho镜像【MS-DOS 7.1+WinPE老毛桃最终修改版】 简体中文版