C++标准的演化
标准库新特性
.h.h标准库版本检测#define __cplusplus 201103L,编译器通常需要打开ISO11使用
Variadic Templates是通过模板参数特征进行递归分化调用处理
void print(){}// 递归终点函数,必须实现
// 参数的数量和类型不确定
template
void print(const T& firstArg, const Type&...args){
cout<
void print(const Type&...args){
}
类参数的递归继承tuple
template class tuple;
template<>class tuple<>{};
// 递归到终点执行上面的
template
class tuple
:private tuple
{
typedef tupleinherited;
public:
tuple(){}
tuple(Head v, Tail... vtail)
:m_head(v), inherited(vtail...){}
typename Head::type head(){return m_head;}
inheried& tail(){return *this;}
protected:
Head m_head;
};
模板空格
vector >; // 传统C++需要空格
vector>; // C++11后不需要空格
C++11可以使用nullptr代替0或者NULL,是一种指针类型
变量类型自动推导关键词auto
// 不使用auto
list c;
list::iterator ite;
ite = find(c.begin(), c.end(), target);
// 使用auto
list c;
auto ite = find(c.begin(), c.end(), target);
C++11支持一致性初始化,在变量后使用大括号进行赋值
int value[]{1, 2, 3};
vector v{2, 3, 5, 6};
vector cities{"Berlin", "New York", "London"};
complexc{4.0, 3.0};
编译器看到{ }便会做到一个initializer_list,它关联到一个array
initializer_list的示例
#include
int i; // 变量声明
int j{};// 变量声明并初始化为0
int *q{};// 变量声明并初始化为nullptr
int x1(5.3);
int x2 = 5.3;
int x3{5.3}; // error, 不可以进行低类型的自动转换
// 参数个数不定的函数调用
void print(std::initializer_list vals){
for(auto p = vals.begin(); p != vals.end(); ++p){
std::cout << *p << "\n";
}
}
print({1,2,2,3}); // 参数数量不确定但是类型相同
initializer_list本质是一个存储参数列表得到array容器,只存储array的头部指针和大小,只能进行浅拷贝
explicit通常只修饰构造函数,表示对象的调用不能进行隐式转换
non-explicit one argument construction才能做隐式转换
struct Comoplex{
int real, imag;
Complex(int re, int im = 0):real(re), imag(im)
{}
Complex operator+(const Complex& x){
return Complex((real + x.real), (imag + x.imag));
}
};
Complex c1(12.5);
Complex c2 = c1 + 5;
range-based for statement
// 语法
for(decl : coll){// (迭代变量:容器),将容器内的变量顺序给迭代变量赋值
statement;
}
// 本质
for(auto _pos = coll.begin(), end = coll.end(); _pos != _end; ++_pos){
decl = *_pos;
statement;
}
// 示例
for(int i: {2,3,4,56,5}){
cout << i << endl;
}
// 改变元素内容并且快速,尽量使用reference
vector vec;
for(auto& elem : vec){
elem *= 3;
}
// 关联式容器不可使用迭代器直接改变元素内容,所以需要使用
如果自行定义了一个ctor(构造函数),那么编译器就不会再给你一个默认的构造函数(不需要实参即可进行调用),在构造函数后写一个=default就可以重新获得并使用default ctor
=default只能用于Big Five(下面代码有),=delete可以用于任何函数身上(=0只能用于virturl函数)
class Foo
{
public:
// 1.普通构造函数
Foo(int i):_i(i){}
Foo() = default;// ctor可以多个并存
// 2. 拷贝构造
Foo(const Foo& x):_i(x._i){}
Foo(const Foo&) = default;// error,不可重载
Foo(const Foo&) = delete;// error,不可重载
// 3. 拷贝赋值
Foo& operator=(const Foo& x){_i = x.i; return *this;}
Foo& operator=(const Foo& x) = default;// error,不可重载
Foo& operator=(const Foo& x) = delete;// error,不可重载
// 4. 普通函数
void func1() = default;// error,普通函数没有默认值
void func2() = delete;
// 5. 析构函数
~Foo() = delete;//
~Foo() = default;
}
空类使用时,编译器会给予默认的版本,通常是public且inline的
// 空类
class Empty{};
// 默认构造函数的显式声明
class Empty
{
public:
Empty(){}
Empty(const Empty& rhs){}
~Empty(){}
Empty& operator=(const Empty& rhs){}
};
如果类内含有指针成员,那么通常需要写出Big-three出来
不可拷贝的类:将拷贝构造和拷贝赋值函数设置为delete
struct NoCopy{
NoCopy() = default;
NoCopy(const NoCopy&) = delete;
NoCopy &operator = (const NoCopy&) = delete;
~NoCopy() = default;
};
// 仅仅向友元开放
class PrivateCopy{
private:
PrivateCopy(const PrivateCopy&);
PrivateCopy &operator=(const PrivateCopy&);
public:
PrivateCopy() = default;
~PrivateCopy();
}
长生对象:对象的析构函数不可被调用,即对象创建后无法被终结
struct NoDotor{
NoDtor() = default;
~NoDtor() = delete;
};
NoDtor nd;// 在作用域内有效,但是超出对象的作用域,销毁对象时调用析构函数会报错
NoDtor *p = new NoDtor();
delete p;// error
Alias Template化名模板:比起宏来更加灵活,可以指定模板参数
template
using Vec = std::vector>;
Vec coll;
// typedef std::vector> Vec;
越简洁越不容易出错,少打字就少出错
通过迭代器萃取对象的类型
template
void test_moveable(Container c){
typedef typename iterator_traits::value_type Valtype;
for(long i = 0; i < SIZE; ++i)
c.insert(c.end(), Valtype());
output_static_data(*(c.begin()));
Container c1(c);
Container c2(std::move(c));
c1.swap(c2);
}
Template template parameter模板模板参数
需要使用化名模板
Type Alias类型化名
// 下面两种使用的方式等价
typedef void(*func)(int,int);// c语言形式
using func = void(*)(int, int);// c++形式
void example(int,int){}
func fn = example;
// 类内的使用
template
struct Container{
using value_type = T;// typedef T value_type
};
template
void fn2(const Cntr& c){
typename Cntr::value_type n;
}
// 化名和typedef在本质上没有不同
using
using namespace std;using _base::variablenoexcept:保证不丢异常
void foo()noexcept;// 等价于void foo()noexcept(true);
// noexcept(条件)放在函数后面,满足条件时表示,保证不丢异常
函数调用发生异常,如果没有处理就会向调用者传递。异常一定要被处理,如果没有处理会向源头传递,如果最终没有处理,std::terminate() 会调用std::abort() 进行程序中断
右值引用的构造函数必须使用noexcept,才能被vector成长的过程调用
class MyString{
private:
char* _data;
size_t _len;
···
public:
// move constructor
MyString(MyString&& str)noexcept
:_data(str._data), _len(str._len){···}
//move assignment
MyString& operator=(MyString&& str)noexcept
{···return *this}
}
override改写/复写:需要函数签名(函数名和参数)完全相同,编译器会检查是否能改写
struct Base{
virtual void vfunc(float){}
};
struct Derived:Base{
// error,已标注override,但是函数签名错误,编译器检查失败
virtural void vfunc(int)override{}
}
final关键字
// 写在类名后,表示该类无法被继承
struct Base final{}
struct Derived:Base{};// error,无法继承
// 写在虚函数后,表示该虚函数不可被重写
struct Base{
virtual void f() final;
};
struct Derived:Base{
void f();// error
}
decltype关键字(类似typeof)
// 1. 用来声明未知返回类型(告诉编译器进行辅助推导)
template
decltype(x+y) add(T1 x, T2 y);
// 2. 适用于模板元编程
typedef typename decltype(obj)::iterator iType;// 提取模板类对象的类型
// 3. 获取匿名函数类型
auto cmp = [](const Person& p1, const Person& p2){
return p1.lastneme() < p2.lastname()||
( p1.lastname() == p2.lastname()&&
pq.firstname() < p2.firstname());
};
std::set coll(cmp);
// 获取容器类型并声明变量
map coll;
···
decltype(coll)::value_type elem;
Lambdas关键字:可以用来定义内联函数和作为参数或本地对象
// 语法
auto 匿名函数名称 = [已有变量或引用](调用时传的参数){
// 函数功能,外部参数在匿名函数内是副本
}
// 用于短函数功能的定义
auto I = []{// 匿名函数的定义
std::cout << "hello lambda" << std::endl;
};
···
I(); // 匿名函数的调用
// 传值(变量可变必须加mutable)
int id = 0;
auto f = [id]()mutable{id++;};
// 传引用
int id = 0;
auto f = [&id](int param){id++; ++param;};
Variadic Templates
// 基本语法
template
void func(const T& firstArg, const Types&... args){
// 处理firstArg
func(args...);
}
// 递归示例:第一个更特化
template
void printX(const T& firstArgs, const Types&... args){
cout << firstArgs << endl;
printX(args...);
}
template
void printX(const Types&...args){
}
print(7.5, "hello", bitset<16>(377), 42);
// C++模拟printf函数
template
void printf(const char *s, T value, Args... args){
while(*s){
if(*s == '%'&&*(++s) != '%'){
std::cout << value;
printf(++s, args...);
return ;
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
// 利用比较两个参数大小的max函数和参数模板进行
int maximum(int n){
return n;
}
template
int maximum(int n, Args... args){
return std::max(n, maximum(args...));
}
// 首位操作方式不同 :
// 1.使用tuple将任意元素组合成一包 2.使用sizeof...()获取元素个数
// tuple的递归继承
template class tuple;
template<> class tuple<>{};
template
class tuple
:private tuple
{
typedef typle inherited;
protected:
Head m_head;
public:
tuple(){}
tuple(Head v, Tail... vtail)
:m_head(v), inherited(vtail...){}
Head head(){return m_head;}
inherited& tail(){return *this;}
};
```
35. 子类的对象具有父类的成分
### 第二讲 标准库
1. Rvalue references右值引用:可以帮助解决非必要的copy,当拷贝的来源是一个右值,可以不必重新分配,而是使用该右值的引用
2. 右值可以认为是一个临时对象,对该临时对象的引用可以看作右值引用,右值被引用后不能再被使用了,只能在GCC2.0之后使用
3. 右值引用示例
```c++
void process(int &i){// 左值传参
cout<< "process(int &&):"<< i < move(偷值):特别是对大的容器的引用,可以更加节省再重新拷贝的时间,但是原来的引用无法使用
尽量使用浅拷贝的引用,可以充分提高效率
容器array
template
struct array{
typedef _Tp value_type;
typedef _Tp* pointer;
typedef value_type* iterator;
// 支持0大小的数组声明
value_type _M_instance[_Nm ? _Nm : 1];
iterator begin(){
return iterator(&_M_instance[0]);
}
iterator end(){
return iterator(&_M_instance[_Nm]);
}
···
// 没有ctor和dtor
}
// 数组声明
int a[100];
// 数组类型简化
typedef int T[100];
T c;
容器hashtable
// G4.9 hash Function的调用获得一个随机数
void *pi = (void)(new int(100));
cout << hash()(123) << endl; // 整性的值hash code和原值相同
cout << hash()(123L) << endl;
cout << hash()(string("Ace")) << endl;// G2.9没有提供
cout << hash()(pi) << endl;
tuple用例
tuple> t;// complex是双倍类型空间
cout << "sizeof=" << sizeof(t) << endl;
tuple t1(41, 6.4, "nico");
cout << "tuple, sizeof = " << sizeof(t1) << endl;
cout << "t1:" << get<0>(t1) << '' << get<1>(t1) << '' << get<2>(t1);
auto t2 = make_tuple(22, 44, "stacy");
get<1>(t1) = get<1>(t2);// tuple的赋值,但是必须类型相同