连续两个右尖括号>中间空格隔开,C++98会优先解析为右移,C++11会优先解析为模板参数界定符
使用std::array替代C数组带来很多好处:总是知道自身大小;不会自动转化为指针;具有迭代器,可以方便的遍历元素
头文件#include
并不能移动任何东西,只是将左值强制转成右值,从实现上相当于static_cast
。
注意:被转换的左值,其生命周期并没有随着左右值的转化而改变,就是说,lvalue的析构不能依赖于move的实现,所以要析构掉就要靠自己
在返回语句中使用std::move(C++20P244):
所以当从函数中返回一个局部变量或参数时,只要return object即可,不要使用std:::move;(N)RVO仅适用于局部变量或函数参数。
可以删除重载函数
class MyClass {
public:
void set_value(double d) {
std::cout << d << endl;
}
void set_value(int) = delete;
};
// 此时,如果调用c.set_value(2);不会发生隐式转换而是编译报错
auto
auto func(T params) -> type
;可以配合decltype关键字:templateauto add(U a, V b) -> decltype(a+b)
// 拷贝列表初始化
T obj = {arg1, arg2, ... };
// 直接列表初始化
T obj{arg1, arg2, ... }
与自动类型推断相结合,拷贝列表初始化和C++17引入的直接列表初始化之间存在重要区别(以下部分未验证):
// C++17后:
auto a = { 11 }; // initializer_list
auto b = { 11, 12 } // initializer_list
auto c{11}; // int
auto d{11,12}; // error
在C++11/14中,拷贝列表和直接列表初始化都将推断出initializer_list<>
// C++11/c++14:
auto a = { 11 }; // initializer_list
auto b = { 11, 12 } // initializer_list
auto c { 11 }; // initializer_list
auto d { 11, 12 }; // initializer_list
delctype 主要用在模板中
在C++98中对非静态成员变量使用sizeof是不能通过编译的,需要对象的实例才能对其成员进行sizeof操作;C++11可以——有什么用?
struct People{ int hand; static People *all; }
People p;
cout << sizeof(p.hand) << endl; // 都支持
cout << sizeof(People::all) << endl; // 都支持
cout << sizeof(People::hand) << endl; // C++11才支持
class MyClass final
派生类中重写父类的虚函数,可以在重写的函数名后加override关键字。这样做的好处是如果该函数不是父类定义的虚函数会报错。如果不是虚函数不能使用override。
final和override说明符出现在形参列表以及尾置返回类型之后
只能使用"=“或”{}"进行初始化;作用先于列表初始化;如果既有列表初始化又有就地初始化,最终值取决于列表初始化
在C++11之前,class和struct的初始化是不同的。
MyClass myobj(10,20,30);
MyStruct myobj = {10, 20, 30};
C++11之后,允许一律使用{…}语法初始化类型。并且等号是可选的
MyClass myobj{10,20,30};
MyStruct myobj{10,20,30};
列表初始化可以用于初始化C++中的任何内容,不局限于结构和类,但是大括号中的值类型必须要一致;还可以对变量进行零初始化,只需要指定一堆空的大括号
int a{3};
int b = {3};
int c {};
使用列表初始化的一个优点是,可以阻止窄化,旧风格会隐式执行窄化。
int x = 3.14; // 隐式窄化,会截断为3
int y { 3.14 }; // 编译报错
标准库中容器对初始化列表的支持源自 #include
这个头文件中initialize_list类模板的支持
初始化列表的使用场景:
initialize_list
模板类为参数的构造函数int makeSum(initializer_list<int> values) {
int total = 0;
for(int value: values) {
total += value;
}
return total;
}
int main() {
int a { makeSum({1, 2, 3})};
int b {makeSum({10, 20, 30, 40, 50})};
std::cout << "a is: " << a << ", b is: " << b << std::endl;
}
编译期常量
const int *p = nullptr;
p是一个指向整型常量的指针constexpr int *q = nullptr;
q是一个指向整数的常量指针参考:C++之内存管理
enum class PieceType : unsigned long {
King = 1,
Queue,
Rook = 10 }
using enum PieceType;
PieceType piece { King };
#include
)数值极限是指当前平台上能取得的最大值,在C中,可以使用各种宏定义,如INT_MAX,这些方法在C++中仍然可以使用,但推荐的做法是使用定义在
中的类模板std::numeric_limits
。
属性是一种将可选的和/或特定于编译器厂商的信息添加到源代码中的机制。在C++对属性进行标准化之前,编译器厂商决定了如何指定此类信息,例如 __attribute__
, __declspec
。从C++11开始通过使用 [[ attribute ]]
对属性进行标准化支持。
[[noreturn]]
这个属性仅适用于函数声明。向函数添加[[noreturn]] 意味着它永远不会将控制权返回给调用点。使用此属性可以避免编译器发出没有返回值的告警。
声明了[[noreturn]]属性的函数如果有返回值将被认为是未定义的行为
[[noreturn]] void forceProgram() {
std::exit(1);
}
std::initializer_list
作为第一个参数class MyClass {
public:
MyClass(int) {}
MyClass(int, int) {}
};
int main() {
MyClass s1({1});
MyClass s2({1, 2});
这里都将执行默认转换,将1和{1, 2}转换成MyClass类型。为避免此类隐式转换,两个构造函数够标记成explicit
class MyClass {
public:
explicit MyClass(int) {}
explicit MyClass(int, int) {}
};
对类的非临时和临时实例上可以调用普通成员函数,C++11中增加引用限定符,可以显式指定能够调用某个方法的实例类型:
class MyClass {
public:
int a { 10 };
char b { 'a' };
int set_value(int d) { return d; }
int getVal() & { return a; } // 只能由非临时实例调用,如果是const函数,&放在const之后, 如:int getVal() const & { ... }
char getChar() && { return b; } // 只能由临时实例调用
};
调用:
int main() {
MyClass c;
cout << "c.set_value(1)" << c.set_value(1) << std::endl; // OK
cout << "MyClass().set_value(2): " << MyClass().set_value(2) << std::endl; // OK
cout << "c.getVal(): " << c.getVal() << std::endl; // OK
cout << "c.getChar(): " << c.getChar() << std::endl; // error
cout << "MyClass().getChar(): " << MyClass().getChar() << std::endl; // OK
cout << "MyClass().getChar(): " << MyClass().getVal() << std::endl; // error
return 0;
}
template < parameter-list > variable-declaration
一个变量模板定义了一个族系的变量,或者静态数据成员
使用前缀0b允许定义二进制字面量,如:int b = 0b101010;
;单引号"'"可以作为分隔符插入数字当中,在编译时会被编译器忽略。如unsigned long long var=1844'6744'0737'0955'0592uLL;
(分隔符的插入可以是随意的)
数字分隔符可以增强数字字面量的可读性
头文件:#include
template< class T, class U = T >
T exchange( T& obj, U&& new_value );
将new_value 赋值给obj并返回obj原来的值。
操作类型可以是普通变量,STL容器,函数指针等。
class stream {
public:
flags_type flags() const { return flags_; }
// Replaces flags_ by newf, and returns the old value.
flags_type flags(flags_type newf)
{ return std::exchange(flags_, newf); }
private:
int flags_ = 0;
};
void f() { std::cout << "f()"; }
void exchange_test () {
stream s;
std::cout << s.flags(12) << '\n'; // 修改flags_的值并返回原值
std::vector<int> v;
std::exchange(v, {1,2,3,4}); // 修改vector的值
std::copy(begin(v),end(v), std::ostream_iterator<int>(std::cout,", "));
void (*fun)();
// the default value of template parameter also makes possible to use a
// normal function as second argument. The expression below is equivalent to
// std::exchange(fun, static_cast(f))
std::exchange(fun,f); // 修改函数指针
fun();
std::cout << "\n\nFibonacci sequence: ";
for (int a{0}, b{1}; a < 100; a = std::exchange(b, a + b))
std::cout << a << ", ";
std::cout << "...\n";
}
可以使用std::exchange很方便的实现移动语义:
MyClass::MyClass(MyClass&& s) {
val = std::exchange(s.val, 0);
}
读写锁,可以同时多个读,做多一个写
[[deprecated]] 将内容标记为已弃用,这意味着仍可以使用它,但不鼓励使用。此属性可以接收一个可选参数,该参数用于解释弃用的原因。
[[deprecated("Unsafe method, please use xyz")]] void func1() { std::cout << "hello" << std::endl; }
如果使用了这个已经弃用的函数将会收到编译错误或警告: