前言
我使用的书籍是 C++标准库(第二版)
前两章是一些简单介绍,就此略过从第三章开始分享我的学习所得,欢迎讨论和指出不足之处
前提
需要有一定的c++基础知识,基础部分概不介绍
如果你的编译器版本过低,那么在运行C++11时会出现bug,这里给出codeblock更新办法
https://blog.youkuaiyun.com/autocyz/article/details/42368147
STL基本知识(大致了解,本书后面章节有详细介绍)
容器,算法,迭代器
https://m.runoob.com/cplusplus/cpp-stl-tutorial.html
模板
https://www.cnblogs.com/yangxiaoping/p/8268209.html
explicit
https://blog.youkuaiyun.com/qq_37233607/article/details/79051075
C++11新特性
1.Template表达式
vector<list<int> >; //过去版本两个表达式闭符之间放一个空格
vector<list<int>>; //C++11可以省略空格
2.nullptr
nullptr是个新关键字,它拥有类型std::nullptr_t,定义于。原来当我们定义一个空指针,会将指针赋值为NULL,但是编译器中会被解释成0,即变成了一个int,而赋值为nullptr,指针不会被转换成整数,一直是一个空指针。
3.auto
C++11允许你声明一个变量或对象(object)而不需要指明其类型,只需说它是auto。
auto i = 42;
double f();
auto d = f();
auto i; //ERROR!由于它的类型由初值自动推导,因此必须有初始化操作
也可以为它加上额外的限定符,当遇到类型很长或表达式很复杂时,auto能发挥很大的作用。
4.一致性初始化与初值列
面对任何初始化动作,你都可以使用大括号
某个local变量属于某种基础类型,也会被初始化为0或nullptr
int values[]{1,2,3};
vector<string> cities{"Berlin","London"};
complex<double> c{4.0,3.0};
int i; //未初始化
int j{}; //初始化为0
int* p; //未初始化
int8 q{}; //初始化为nullprt
窄化(narrowing)对大括号不成立
int a(5.3);
int b{5.3}; //ERROR
char c{7};
char d{99999}; //ERROR
vector<int> v1 {1,2,3};
vector<int> v2 {1.0,2,3}; //ERROR
用户自定义类型之初值列
void print (initializer_list<int> vals)
{
for(auto p=vals.begin();p!=vals.end();++p){
cout << *p << endl;
}
}
print ({12,3,5,7,11,13,17});
当“指明实参个数”和“指明一个初值列”的构造函数(ctor)同时存在,带有初值列的那个版本胜出
class P
{
public:
P(int,int); //P p(77,5);
P(initializer_list<int>); //大括号优先使用
};
explicit构造函数如果接受的是个初值列,会失去“初值列带有0个、1个或多个初值“的隐式转换能力
5.Range-Based for循环
C++11引入了一种崭新的for循环形式,可以逐一迭代某个给定的区间、数组、集合内的每一个元素。语法如下:
for( decl : coll ){
statement
}
//例:
for(int i: {2,3,4,5,6}){
cout << i << endl;
}
//等同于
for(auto _pos=coll.begin(),_end=coll.end();_pos!=_end;++_pos){
decl = *_pos;
statement;
}
此外,允许你用寻常的、大小已知的C-style array
int array[] = {1,2,3,4,5};
long sum = 0;
for(int x : array){
sum += x;
}
当元素在for循环中被初始化为decl,不得有任何显式类型转换
6.Move语义和Rvalue Reference
这部分书上是简略介绍,没怎么看懂,推荐这个博客:
https://www.cnblogs.com/harrywong/p/4220233.html
7.新式的字符串字面常量(String Literal )
(1)Raw String Literal允许我们定义字符串序列,以 R"( 开头,以 )" 结尾,可以内含line break,例如:
//表示两个反斜杠和一个n
"\\\\n"
R"(\\n)"
如果要在raw string内写出 )" ,可使用定义符。因此一个完整raw string的完整语法是R"delim(…)delim",delim是个字符序列,最多16个基本字符,不可含反斜线、空格和小括号,例:
R"nc(a\
b\nc()"
)nc";
//等同于
"a\\\n b\\nc()\"\n "
定义正则表达式时raw string literal特别有用
(2)编码的(Encoded)String Literal
只要使用编码前缀,你就可以为string literal定义一个特殊的字符编码。下面这些编码前缀都预先定义好了:
- u8定义一个UTF-8编码。以UTF-8编定的某个给定字符起头,字符类型为const char
- u定义一个string literal,带着类型为char16_t的字符
- U定义一个string literal,带着类型为char32_t的字符
- L定义一个wide string literal,带着类型为wchar_t的字符
例如:
L"hello"
Raw string开头那个R的前面还可以放置一个编码前缀
8.关键字noexcept
用来指明某个函数无法/不打算抛出异常
写法为
void foo() noexcept
编译器在处理noexcept函数时可以进行一些优化。因为编译器知道函数不会抛出异常,它可以进行一些针对性的优化,以提高程序的性能和效率。
异常传播:如果在noexcept函数内部调用了另一个函数,并且该函数抛出了异常,那么在noexcept函数中,异常将被立即转发(propagate)给上一级调用栈。这意味着异常将直接传递给调用noexcept函数的代码,并不会被noexcept函数捕获或处理。
需要注意的是,noexcept并不意味着函数绝对不会引发异常。如果在noexcept函数内部使用了不受noexcept限制的代码,比如调用了另一个不带noexcept的函数,或者使用了可能引发异常的库函数,那么异常仍然可能被引发。因此,在使用noexcept关键字时,需要确保函数的实现不会导致异常抛出。(在c++17中有对此关键字的补充)
9.关键字constexpr
用于指示变量或函数在编译时可以被计算出来。它可以用于修饰变量声明、函数声明和函数定义。
//num值在编译时就被确定为42
constexpr int num = 42;
//修饰的函数可以在编译时进行计算。这意味着函数可以在编译期间返回一个常量值
constexpr int add(int a, int b)
{
return a + b;
}
总的来说,constexpr关键字用于在编译时进行常量计算和优化。它可以应用于变量和函数,使得编译器可以在编译时进行更多的优化和静态检查,提高程序的性能和可靠性。使用constexpr可以编写更加高效、安全和可预测的代码。
10.新的Template特性
- C++11允许模板参数列表接受可变数量的参数。这意味着可以定义接受任意数量参数的模板函数或类模板。使用…语法表示变长参数
template<typename... Args>
void myFunction(Args... args) {
// 使用变长参数进行操作
}
- C++11允许使用using关键字定义模板别名。这样可以为模板参数列表定义一个别名,使得代码更加清晰易读
template<typename T>
using MyContainer = std::vector<T>;
这里定义了一个名为MyContainer的模板别名,它是std::vector的别名。这样可以使用MyContainer作为类型名称来代替std::vector。
- C++11改进了对函数模板参数的推导规则,使得在某些情况下可以自动推导出函数模板的参数类型,无需显式指定。这样可以简化函数模板的使用
template<typename T>
void printSize(const T& container) {
std::cout << "Size: " << container.size() << std::endl;
}
std::vector<int> vec = {1, 2, 3};
printSize(vec); // 不需要显式指定模板参数类型
- C++11允许在模板参数列表中指定默认参数值。这样可以为某些模板参数提供默认值,使得在使用模板时可以省略这些参数
template<typename T = int>
void printValue(T value) {
std::cout << "Value: " << value << std::endl;
}
printValue(42); // T被推导为int类型
printValue(3.14); // T被推导为double类型
11.Lambda
它是一种简洁而强大的函数定义方式。Lambda表达式可以用于创建匿名函数,这些函数可以在需要的地方被直接调用,而无需显式地定义函数名称。
[capture-list] (parameters) -> return-type {
// 函数体
}
- Capture-list(捕获列表):它可以为空,或者使用方括号 [] 包含一个或多个捕获表达式,用于捕获外部变量。捕获列表控制了Lambda表达式内部对外部变量的访问权限。
- Parameters(参数):类似于普通函数的参数列表,用于传递参数给Lambda表达式。
- Return-type(返回类型):指定Lambda表达式的返回类型,可以省略,编译器会自动推断返回类型。
- 函数体:实际的函数实现部分,包含在花括号 {} 中。
一个简单例子:
// Lambda表达式示例
auto sum = [](int a, int b) -> int {
return a + b;
};
// 使用Lambda表达式
int result = sum(5, 3); // 调用Lambda表达式,返回 8
12.decltype
用于推断表达式的类型。decltype关键字可以用于获取变量、表达式或函数调用的类型,并将其作为一个类型标识符使用。
decltype(expression)
其中,expression是一个要进行类型推断的表达式。decltype返回expression的类型。
int x = 5;
decltype(x) y = 10; // y的类型推断为int
double calculate(double a, double b) {
return a * b;
}
decltype(calculate) multiply = calculate; // multiply的类型推断为函数指针类型
std::vector<int> numbers;
decltype(numbers)::value_type z = 20; // z的类型推断为vector<int>::value_type,即int
- 第一个示例中,使用decltype(x)推断变量x的类型,并将其赋值给变量y。由于x的类型是int,所以y的类型也是int。
- 第二个示例中,使用decltype(calculate)推断函数calculate的类型,并将其赋值给函数指针multiply。multiply的类型与calculate函数的类型一致。
- 第三个示例中,使用decltype(numbers)::value_type推断容器numbers的元素类型,并将其赋值给变量z。由于numbers是一个std::vector类型的容器,所以z的类型是int。
decltype在模板编程和泛型编程中特别有用,可以根据表达式的类型进行编译时类型推断,从而增加程序的灵活性和泛化能力
13.Enumeration
枚举类(Scoped Enumerations):C++11引入了枚举类,也称为作用域枚举。枚举类在定义时使用关键字enum class,并将枚举值限定在枚举类的作用域内,不会与其他作用域中的枚举值冲突。枚举类提供了更严格的类型安全性。
enum class Color { Red, Green, Blue };
Color c = Color::Red;
- 枚举的底层类型指定(Underlying Type Specification):可以为枚举类型指定底层整数类型。默认情况下,枚举的底层类型为int,但可以使用关键字enum后的冒号来指定其他整数类型。
enum class Status : short { Success, Failure };
- 强类型枚举(Strongly Typed Enumerations):强类型枚举是指在枚举类型前加上关键字enum,并且不能进行隐式的整数转换。强类型枚举提供了更严格的类型检查和更明确的值范围。
enum class ErrorCode : int { None = 0, InvalidInput = 1, OutOfMemory = 2 };
ErrorCode error = ErrorCode::InvalidInput;
- 枚举的正规化(Enum Class Regularization):C++11修复了C++98中枚举类型的一些问题。例如,C++11确保枚举类型是一个完整的类型,并可以进行前向声明。
这些特性增强了C++中的枚举类型的功能和灵活性。它们提供了更好的类型安全性、更明确的枚举值作用域和更明确的值范围。这使得枚举类型在编程中更加可靠和易于使用。