删除的函数不能通过任何方式被使用
方便起见,删除函数被声明为公有的,而不是私有的。这样设计的原因是,使这个函数为公有的可以产生更易读的错误信息
任何函数都可以是删除的,然而仅有成员函数才可以是私有的
delete
能够防止隐式转换(见第一部分侯捷C++ 九)——通过把隐式转换的重载函数都delete
,避免隐式转换,充分利用了delete
和重载函数决议的规则delete
可以避免不必要的模板具现化template<typename T> void processPointer(T* ptr)
void*
和char*
做处理(这两个指针都较为特殊,前者是因为没有办法对它们解引用,递c
类型的字符串,而void*
不是指向独立字符的指针),因此我们必须禁止void*
和char*
的具现化,这个时候只要结合模板特化和delete
即可实现这一功能。template<> void processPointer<void>(void*) = delete; template<> void processPointer<char>(char*) = delete;
void*
和char*
了,而这个功能是C++98无法做到的, 对于C++98来说实现的唯一途径就是借助于成员函数的访问权限,然后模板特化是无法在类的作用域内定义的。delete
来删除函数替换放在私有作用域中未定义的;
基类中的函数被声明为虚的。
基类中和派生出的函数必须是完全一样的(除了虚析构函数)。
基类中和派生出的函数的参数类型必须完全一样。
基类中和派生出的函数的常量特性必须完全一样。
基类中和派生出的函数的返回值类型和异常声明必须是兼容的。
函数的引用修饰符必须完全一样。成员函数的引用修饰符是很少被提及的的C++特性,这些修饰符使得将这些函数只能被左值或者右值使用成为可能。成员函数不需要声明为虚就可以使用它们。引用标识符有两个,一个是 &
,用这个修饰的成员函数只允许被*this
是左值来调用。另外一个是&&
,值允许被*this
是右值来调用。
class Widget{ public: ... void doWork() &; //只有当*this为左值时 //这个版本的doWorkd()函数被调用 void doWork() &&; //只有当*this为右值 //这个版本的doWork()函数被调用 }; Widget makeWidget(); //工厂函数,返回右值 Widget w; //正常的对象(左值) ... w.doWork(); //为左值调用Widget::doWork() 即Widget::doWork & makeWidget().doWork(); //为右值调用Widget::doWork() 即Widget::doWork &&
class Base { public: virtual void mf1() const; virtual void mf2(int x); virtual void mf3() &; virtual void mf4() const; }; class Derived: public Base { public: virtual void mf1() const override; virtual void mf2(int x) override; virtual void mf3() & override; void mf4() const override; // 加上"virtual"也可以,但是不是必须的 };
const_iterator
而不是iterator
const_iterator
const_iterator
既容易获得也容易使用cbegin()
和cend
可以产生const_iterator
,甚至非const
容器也可以这样做const_iterator
来进行定位(即插入与删除)template <typename C, typename V> void findAndInsert(C& container, const V& targetVal, const V& insertVal) { using std::cbegin; using std::end; auto it = std::find(cbegin(container), cend(container), targetVal); container.insert(it, insertVal); }
STL
容器了。很可惜上面的代码需要C++14的支持,C++11只支持begin
和end
,而到C++14才开始支持cbegin
,cend
。如果你的编译器不支持C++14,那么你可以使用下面这段代码代替。const_iterator
替换iterator
begin
、end
、cbegin
、cend
等
在任何时候,只要知道函数不会产生异常,就声明为noexcept
int f(int x) throw() // C++98 style —— less optimizable int f(int x) noexcept // C++ 11 style —— most optimizable
throw()
来表示不抛出异常的情况下,如果函数抛出异常会导致栈解旋,然后程序结束这也就是所谓的 less optimizablenoexcept()
的情况下,程序会直接结束。这两种形式对最后生成的代码有很大的影响。对于后者就不需要生成一些用于保护堆栈的代码了,因为不需要解旋,所以对代码优化友好—— most optimizablestd::vector
元素见第二部分二十三、二十五、二十六;
为了优化vector内部的元素拷贝,C++11是将原来空间中的元素一个个move
到新的空间中,如果在move
的过程中发生了异常,那么这将是一个不可逆的过程,并且失去了C++98中异常安全保证的能力,因此C++11需要知道move
操作是否是一个不会抛出异常的操作,如果是才会使用move
;
这会带来性能上的提升,否则就降级到C++98的场景下,通过拷贝来实现,保证异常安全。如果move
是用了noexcept
关键字声明的,那么就可以使用标准库提供的noexcept()
函数来检测move
是否会抛出异常,而这个在C++98中是做不到的。
noexcept
是函数接口的一部分,这意味着调用者会依赖它;noexcept
声明的函数相比于没有使用noexcept
声明的函数代码更具可优化性;noexpect
对于move
、swap
、内存分配函数、析构函数等具有特别的价值;noexcept
。
constexpr
constexpr
用于对象时,它本质上是const的一种增强形式constexpr
用于函数时:constexpr
修饰的函数可以使得该函数可以在编译期运行,产生一个编译期的值,也可以像普通的函数一样在运行期执行,然后产生一个运行期的值。这很大程度上取决于传入的参数是编译期的值还是运行期的值。void
都是LiteralType
类型,自定义类型也可以是LiteralType
类型,这取决于其自定义类型的构造函数是否是constexpr
。class Point { public: constexpr Point(double xVal = 0, double yVal = 0) noexcept : x(xVal),y(yval) {} constexpr double xValue() const noexcept { return x; } constexpr double yValue() const noexcept { return y; } void setX(double newX) noexcept { x = newX; } void setY(double newY) noexcept { y = newY; } private: double x,y; };
constexpr
修饰的值不仅仅是一个常量值,而且还是一个编译期就知道的值const
的区别const
修饰的变量可以是编译期的值,也可以是运行期的值。而constexpr
必须是编译期的值
const
只能用于修饰类的成员函数,而constexpr
可以修饰普通的函数,并且两者的含义完全不同。
所有constexpr
对象都是const
,但不是所有const
对象都是constexpr
int sz; constexpr auto arraySize1 = sz; // error sz's value not known at compilation std::array<int, sz> data1 // error array size need compilation value constexpr auto arraySize2 = 10; // fine std::array<int, arraySize2> data2; // fine
希望编译器保证变量的值可以在需要编译时常量的上下文中使用,那么需要使用的工具是 constexpr
,而不是 const
。
C++中有些地方必须要传入编译期的值,比如数组的大小,˙整型模版参数(标准库的bitset容器),枚举值,对齐大小等等。在这些地方如果你想传入一个编译期的值,const
是无法保证的,除非你确定你const
修饰的变量都是编译期的值,倘若后面修改代码使得变成非编译期的值,将会导致大量编译错误,而这些地方使用constexpr
是最万无一失的了,因为编译器会确保constexpr
是编译期值。
constexpr
对象是const
,它的初始值是编译期的;constexpr
函数当传入的参数是编译期值时可以产生编译期的结果;constexpr
对象和函数可以广泛使用在非constexpr
修饰的对象和函数上下文中;constexpr
关键字是对象以及函数接口的一部分。