Item 9 优先考虑 using 而不是 typedef
条款9:优先考虑别名声明而⾮typedef有时候一些很复杂又很多地方能用到的类型或者声明,比如 std::unique_ptr<std::unordered_map<std::string,std::string>> 这样的类型,每个地方都要写的话太恐怖了,我们可以引入 typedef:
12typedef std::unique_ptr<std::unordered_map<std::string, std::string>>UPtrMapSS;
但是呢 typedef 毕竟是 C++98 的东西了,或多或少有点过时,虽然他可以在 C++11 中工作,但是 C++11 也提供了一个别名声明:
1using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;
由于这⾥给出的 typedef 和别名声明做的都是完全⼀样的事情,我们就有理由想知道会不会出于⼀些技术上的原因所以两者中有⼀个更好?
这⾥就不得不提一下代码的 ...
Item 8 优先使用nullptr
条款8:优先考虑nullptr而非0和NULL很明显的一个问题:字面值 0 是一个 int 型的整数,不是一个指针
如果 C++ 发现当前上下文只能使用指针,他才会把 0 解释为指针,但那属于最后的退路,一般来说 C++ 的解析策略是把 0 看作 int 而不是一个指针
实际上 NULL 也是这样的。但是 NULL 的实现细节有些不确定因素,因为实现是被允许给NULL⼀个除了 int 之外的整型类型(⽐如 long)。这不常⻅,但也算不上问题所在。这⾥的问题不是NULL没有⼀个确定的类型,而是 0 和 NULL 都不是指针类型。
在 C++98 中,对指针类型和整型进⾏重载意味着可能导致奇怪的事情。如果给下⾯的重载函数传递 0 或 NULL,它们绝不会调⽤指针版本的重载函数:
1234567void f(int); //三个f的重载函数void f(bool);void f(void*);f(0); //调⽤f(int)而不是f(void*)f(NULL); //可能不会被编译,⼀般来说调⽤f(int),绝对不会调⽤f(void*)
而 f(NULL) 的不确定⾏为是由 NULL 的 ...
右值引用
前言直接引用白老师的话:在开始回答问题之前,我们首先要说明:C++11 引入的移动语义,本身逻辑其实是非常的简单的,但是新增的值类别和其规则是较为繁杂的,很多人之所以不会并且对移动语义抱有错误的幻想,无非是被一些互联网的错误想法影响的。
什么是右值引用?右值引用就是引用一个右值,和左值引用一样,算是一种引用。C++11 引入了右值引用,并且完善了一整套规则:值类别:即左值,纯右值,亡值。这部分推荐深入学习
其实简单来说,就是将各种表达式分类,哪些是左值表达式,哪些是右值(纯右值和亡值都是右值)。右值引用只能被右值表达式初始化。
举个例子:
1234567891011struct X{ X() = default; X(const X& x){ // ...todo 进行资源的复制 } X(X&& x)noexcept { // ...todo 进行资源的移动 }private: // ...todo 很多的数据成员};
在右值引用(移动语 ...
右值,右值引用,移动语义相关
C++ 居然可以给右值赋值?1234567891011121314#include <iostream>struct A { };A f(){ return {};}int main() { A a; f() = a;}
这段代码没有报错,这里的 f() 应该是一个纯右值表达式,因为它是一个返回类型是非引用的函数调用。
类实际上会隐式声明一个复制赋值运算符,如果把它 delete 掉,还能成功运行吗?
1A& operator=(cosnt A&) = delete;
就不能了,因为 右值不能用作内建赋值运算符及内建复合赋值运算符的左操作数。 例子中的 f() = a的 = 不是内建的函数
移动语义相关12345678#include <iostream>int test(int&&) {}int main(){ int &&a = 1; test(a ...
只在栈上生成对象的类
勘误:在C++语言层面,没有办法实现符合这种要求的类,即使将 new 和 delete 重载为私有,加有限定名字查找 :: ,优先查找全局的 operator new 和 operator delete,重载为私有的就毫无意义。就算把new和delete设置为= delete也不行。详见https://godbolt.org/z/nqs9Gcv6b。
1234567891011121314151617#include <iostream>struct X{ int n{}; X() { puts("X()"); } X(int v):n{v} {puts("X(int)");} ~X() { puts("~X()"); }private: void* operator new(size_t)noexcept {return nullptr;} ...
成员函数什么时候以 const 修饰
举例123456class Date {public: Month month() const; // 好 int month(); // 不好 // ...};
第一个 month() 比第二个有更多的信息,以 const 修饰,代表不会修改当前的日期,返回类型 Month 也非常明确。
const 成员函数修饰是为什么,能做什么。大多数情况下默认不修改当前类的数据成员的话,就要加 const,明确语义,可以增加可读性。 但是应该不止如此。
123456789101112131415161718#include <iostream>class Date { using Month = int;public: Month m; int month() { return m; } };void func(const Date& date){ std::cout << date.month() <&l ...
所有权和移动
参考文章
前言首先有那么几个问题:从 C++98 升级到 C++11 能提升性能吗?从函数中返回 STL 容器的开销大吗?return std::move(x) 有意义吗?这些问题都牵扯到了移动语义和复制构造。
解释1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#include <iostream>#include <string>struct X { X() { puts("X()"); } X(const X&) { puts("X(const X&)"); } X(X&&)noexcept { puts("X(X&&)"); } ~X() { puts("~X()&qu ...
继承和多态
继承体系中基类的初始化顺序和什么有关?(类的继承顺序)继承里面谁先被继承,则谁先被初始化
1234567891011121314151617181920212223242526272829class A {public: A(const char* s) { cout << s << endl; } ~A() {}};class B : virtual public A{public: B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }};class C :virtual public A{public: C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }};class D :public B, public C//继承 ...
抽象类
抽象类是什么定义不能被实例化,但可以用作基类的抽象类型。
1234567891011121314151617struct Base{ virtual int g(); virtual ~Base() {}}; struct A : Base{ // OK:声明三个成员虚函数,其中两个是纯虚函数 virtual int f() = 0, g() override = 0, h(); // OK:析构函数也可以是纯虚函数 ~A() = 0; // 错误:函数定义中的纯说明符 virtual int b() = 0 {}};
解释抽象类用于表示一般性概念 (例如 Shape、Animal 等) ,它可以用作具体类 (例如 Circle、Dog 等) 的基类。
除了作为从其派生的类的基类子对象之外,不能创建抽象类的对象,且不能声明抽象类类型的非静态数据成员。
抽象类型不能用作形参类型,函数返回类型,或显式转换的类型(注意,这是在函数定义点和函数调用点检查的,因为 ...
Item 1 理解模板类型推导
条款1:理解模版类型推导考虑这样一个函数模版:
12template<typename T>void f(ParamType param);
它的调⽤看起来像这样
1f(expr); //使⽤表达式调⽤f
在编译期间,编译器使⽤ expr 进⾏两个类型推导:⼀个是针对 T 的,另⼀个是针对 ParamType 的(ParamType 包括了 const 和引⽤的修饰)。举个例⼦,如果模板这样声明:
12template<typename T>void f(const T& param);
然后这样进⾏调⽤
12int x = 0;f(x); //⽤⼀个int类型的变量调⽤f
显而易见的 T 被推导为 int ,ParamType 却被推导为 const int&
还是这个例子
1234template<typename T>void f(ParamType param);f(expr); //从expr中推导T和ParamType
分别有三种情况:
ParamType 是⼀个指针或引⽤,但不是通⽤引⽤(关于通⽤引⽤请参⻅ ...