IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> Lesson2 -> 正文阅读

[开发工具]Lesson2

接口

回顾一下 Human 和 Student:

class Human {	// 虚基类
public:
	virtual ~Human() {};	// 不能是纯虚函数
	virtual void talk() = 0;	// 纯虚函数,没有函数体
};

class Student final : public Human {
public:
	Student(const std::string& name) : name_(name) {}

	void talk() override {	// 重写父类的纯虚函数,有函数体
		std::cout << fmt::format("Student {} talk.\n", name_);
	}

private:
	std::string name_;
};

定义了纯虚函数的类是虚基类,虚基类的作用是当成接口给其他用户使用,其他用户只关心该类提供了什么功能,而不用关心实现的细节,甚至不用关心实际类型。Human 定义了纯虚函数 talk(),它的函数体是由子类 Student 去实现的。

注意,析构函数不能是纯虚函数。这是因为子类(Student)在析构的时候会调用父类(Human)的析构函数,来销毁父类子对象。如果父类的析构函数是纯虚函数(换句话说,没有实现;再换句话说,没有函数体),那么父类就没有办法销毁了。

Dynamic_cast

虽然 Student 和 Teacher 都继承自 Human,但是它们可能各自有独特的能力。比如,Student 可以学,Teacher 可以教:

class Student final : public Human {
public:
	// ...
	void learn() {	// Student 独特的函数
		std::cout << fmt::format("Student {} learn.\n", name_);
	}
	// ...
};

class Teacher final : public Human {
public:
	// ...
	void teach() {	// Teacher 独特的函数
		std::cout << fmt::format("Teacher {} teach.\n", name_);
	}
	// ...
};

现在我们有一个函数,根据输入对象的实际类型,来调用它独特的函数:

// 根据输入的实际类型,调用不同的函数
void do_something(Human* ph) {
	// 尝试将 Human 转换成 Student
	if (auto ps = dynamic_cast<Student*>(ph); ps) {
		ps->learn();	// 调用 Student 独特的函数
	}
	// 尝试将 Human 转换成 Teacher
	else if (auto pt = dynamic_cast<Teacher*>(ph); pt) {
		pt->teach();	// 调用 Teacher 独特的函数
	}
}

Dynamic_cast() 函数用来做类型转换,如果输入对象的实际类型不是我们希望的类型,则转换失败,得到空指针;如果输入对象的实际类型是我们希望的类型,则转换成功。

统一初始化

C++11 之前对象初始化都是小括号,会带一些问题:

class Human {};
Human h();	// error

我们定义了 Human 类型,接着希望定义一个 Human 类型的对象 h。但是编译器会报错,因为编译器把对象定义当成了函数声明,函数名是h,参数是空,返回值是 Human 类型。

C++11 引入了大括号初始化,帮助编译器做出正确的选择:

class Human {};
Human h{};	// correct

如果我们想使用字符串 “abc” 来初始化一个 std::string 对象,下面 3 种写法是等价的:

std::string s1("abc");		// 小括号初始化
std::string s2 = "abc";		// 赋值初始化(隐式构造)
std::string s3{ "abc" };	// 大括号初始化

注意,我们一般使用大括号初始化,可以方便地区分变量和函数。

C++11引入了初始化列表 std::initializer_list,帮助我们更方便地完成初始化操作,它的底层实现是数组。我们可以使用初始化列表来替换上面的 “abc”:

std::initializer_list<char> il = { 'a', 'b', 'c' };
std::string s4{ il };	// 把元素 'a','b','c' 添加到容器 s4 中

我们也可以把临时的初始化列表对象当成参数:

std::string s4{ { 'a', 'b', 'c' } };

甚至可以省略外面的大括号:

std::string s5{ 'a', 'b', 'c' };

大括号即可给普通对象初始化,也可以给容器初始化,所以大括号初始化也叫做统一初始化。

注意,下面两个初始化是不一样的:

std::string s6(3, 'a');		// 添加 3 个 'a' 到容器 s6 中
std::string s7{ 3, 'a' };	// 添加 3 和 'a' 到容器 s7 中

右值语义

请添加图片描述

左值可以放在等号左边,右值可以放在等号右边:

  • 左值:可以获取地址。
  • 将亡值:右值引用。
  • 纯右值:字面量,临时变量。

引用分为左值引用和右值引用。左值引用只能绑定到左值,右值引用只能绑定到右值。但是,常量左值引用可以绑定到右值。C++提供了 std::move() 函数,来将左值转换成右值引用。左值被转成后就不应该再使用了,因为它的资源可能已经移动走了。

右值的用处:

  • 提高效率。大多数情况下,移动的效率比复制高。复制是将对象管理的资源做了一份拷贝(深拷贝),而移动则是将资源转交给了其他对象(浅拷贝)。
  • 移动语义。有些类型无法将管理的资源进行拷贝,比如套接字、数据库连接。传递这种类型只能使用移动,不能使用复制。

自动类型推导

Auto 可以根据初始化的输入来推导变量的类型,先来看输入是左值的情况:

int n = 1024;	// 左值

auto x1 = n;	// int
auto& x2 = n;	// int&
auto&& x3 = n;	// int&

有 3 种形式:

  • x1 的类型就是 n 的类型,与 n 的 CV 限定符无关,与 n 是左值还是右值无关。
  • x2 的类型是左值引用,只能绑定到左值,并且与 n 的 CV 限定符有关。
  • x3 是万能引用,类型取决于 n 是左值还是右值。如果 n 是左值,则 x3 是左值引用;如果 n 是右值,则 x3 是右值引用。由于 n 是左值,x3 是左值引用。

再来看输入是右值的情况:

auto x1 = 1024;			// int
const auto& x2 = 1024;	// const int&
auto&& x3 = 1024;		// int&&

同样有 3 种形式:

  • x1 的类型就是 1024 的类型。
  • x2 的类型是常量左值引用,可以绑定到右值。注意,不能去掉 const,因为非常量左值引用只能绑定到左值。
  • x3 是万能引用,由于 1024 是右值,x3 是右值引用。

字面量

Auto 可以搭配字面量使用:

auto x1 = 0;		// int
auto x2 = 0u;		// unsigned int
auto x3 = 0l;		// long
auto x4 = 0ul;		// unsigned long
auto x5 = 0.0f;		// float
auto x6 = 0.0;		// double
auto x7 = "abc";	// const char*

还可以表示时间:

#include <chrono>

using namespace std::literals;

auto t1 = 1s;	// std::chrono::seconds
auto t2 = 1ms;	// std::chrono::miniseconds
auto t3 = 1us;	// std::chrono::microseconds
auto t4 = 1ns;	// std::chrono::nanoseconds
  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2022-01-17 11:41:39  更:2022-01-17 11:41:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/25 13:09:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计