C++2.0一些特性
std::function
std::function的实例可以对任何可以调用的目标实体进行存储、复制和调用操作。这些目标实体包括普通函数、Lambda表达式、函数指针以及其它函数对象等。(升级版函数指针)
//普通函数
int MyFunc1(int a,int b){}
function<int(int,int)> functor1 = MyFunc1;
functor1(7,2);//lambda
auto MyFunc2 =[](int a,int b){return a + b;};
function<int(int,int)> functor2 = MyFunc2;
functor2(2,5);//仿函数
struct MyFunc3{int operator() (int a,int b){}
};
function<int(int,int)> functor3 = MyFunc3;
functor2(2,9);
智能指针
new分配内存,智能指针可以自动释放。而传统指针需要手动delete。
智能指针对普通的指针进行封装,负责自动释放所指的对象,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命期。
- auto_ptr(c++11弃用)
auto_ptr<string> p1(new string("I reigned loney as a cloud."));
auto_ptr<string> p2;
p2=p1; //auto_ptr不会报错
此时p2剥夺p1所有权。访问p1会报错。(存在内存崩溃的风险)
unique_ptr
实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。避免资源泄露
unique_ptr<string> p3(new string("I reigned loney as a cloud."));
unique_ptr<string> p4;
p4 = p3; //error
/*****************************
当将一个 unique_ptr 赋值给另一个
如果源 unique_ptr 是个临时右值
编译器允许这么做
否则 报错
*****************************/
unique_ptr<string> ps1, ps2;
ps1 = make_unique<int>(200);
ps2 = move(ps1);
shared_ptr
实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。用计数机制来表明资源被几个指针共享。
多个线程同时读同一个shared_ptr对象是线程安全的,但是如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。shared_ptr拥有成员函数如下:
- use_count — 引用计数的个数
- unique — 是否独占
- swap — 交换两个shared_ptr所拥有的对象
- get — 返回内部对象
weak_ptr
- 尝尝与shared_ptr搭配使用,为shared_ptr的观察者。
- 不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。
- 当 shared_ptr 管理的资源被释放时,weak_ptr 会自动变成 nullptr。
- 只是提供了对管理对象的一个访问手段。用来解决shared_ptr相互引用时的死锁问题
类型转换
const_cast
— 将const type
转化为type
(type为指针或引用)static_cast
— 可以用于各种编译器认可的隐式转换(类对象向上转换)dynamic_cast
— 用于含有虚函数的类的转换,用于类向上和向下转换reinterpret_cast
— 任意类型转换,不保证成功。
//static_cast
int a = 5;
double b = static_cast<int>(a);class Base{ };
class Derive:Base{ };
Base* ptr;
Derive *temp = new Derive();
ptr = static_cast<Base*>(temp);//const_cast
const int a = 1;
int *p = const_cast<int*>(&a);
Variadic Template
数量不定的模板参数。
...
用来表示一包pack
。表示模板参数、函数参数、函数参数类型的数量不确定。(用来递归)
void print() {}//递归出口
template<typename T,typename... Types>
void print(constT& firstArg,const Types&... args) {cout <<firstArg<<endl;print(args...);//sizeof...(args)可以知道当前剩余几个
}//tuple
template<typename... Values>class tuple;
template<> class typle<>{};template<typename Head,typename... Tail>
class tuple<Head,Tail...>:private tuple<Tail...>
{protected:Head m_head;
};
/*******************************************
X<T1,T2,T3> ==> X<T2,T3> ==> X<T3> ==> X<>T1 t1 ==> T2 t2 ==> T3 t3 ==> NULL
*******************************************/
作用域
- 函数模板
- 类模板
变化的是模板参数
- 参数个数(参数个数逐一递减,实现递归)
- 参数类型(参数类型随参数个数也变化)
重写printf
//无参数版本
void printf(const char* s) {while(*s) {if(*s == '%' && *(++s)!='%')//还有参数,则抛出异常throw std::runtime_error("xxx");std::cout<<*s++;}
}
template<typename T,typename... Args>
void printf(const char*,T value,Args... args){while(*s){if(*s == '%' && *(++s)!='%') {//拿类型std::cout << value; //输出printf(++s,args...);//跳过 d,s,f等 继续递归return;}std::cout<<*s++;}throw std::logic_error("xxx");
}int *pi = new int;
print("%d%s%p%f\n",15,"Hello World",pi,3.1415926);
递归继承 — tuple
Spaces in Template Expressions
模板表达式中的空格。
在过去的版本中,vector<vector<int> > res
,右侧的> >
中间必须有空格,不然编译器会以为是个操作符。该版本之后中间的空格可以取消。
nullptr
C++11支持使用 nullptr
代替0 NULL
,其类型为std::nullptr_t
void func(int);
void func(void*);func(0);//调用func(int)
/************************
c++11之前, #define NULL 0
#define NULL ((void *)0)
*************************/
func(NULL);
func(nullptr);//调用func(void*)//nullptr定义
typedef decltype(nullptr) nullptr_t;
Automatic Type Deduction
auto
自动类型推断。在模板的应用中,我们已经知道编译器可以推导实参的类型,在此将此能力表现。
名称特别长懒得打字、类型复杂一时想不出时推荐使用。
auto i = 42;
vector<int> vec;
auto pos = vec.begin();
auto tmp = [](int x)->bool{};
Uniform Initialization
一致性初始化。
在C++11之前,初始化可能会发生在()、{}、=
操作中。程序员很容易困惑于:初始化变量、对象时怎么写?。基于此原因,导入uniform initialization
。保证任何初始化都可以使用一种语法{}
//一致性初始化
int value{1,2,35,4};
vector<string> vec{"asfg","asgg","ewgs"};
complex<double> comp{4.0,3.0};
initializer_list
编译器看到{..}
便生成initializer_list<T>
,其内部关联到容器array<T,n>
。调用构造函数时(初始化时调用的是构造函数ctor
),该array
内的元素可被编译器分解,逐一传给函数。
函数参数如果是
initializer_list<T>
,调用者不能传入多个initializer_list<T>
。可以利用
{}
来赋空值
int i{}; // i == 0
int* ptr{}; // ptr == nullptr
- 不推荐向低精度转化
int x{5.0}
自定义函数时,如果想通过简单的方式来接收任意个数参数,可以使用initializer_list
。
比起
...
参数的类型比较严格。
void print(std::initializer_list<int> vals) {for(auto p = vals.begin();p!=vals.end();p++) {std::cout << *p << " ";}
}
//调用
print({12,53,26,57,14});
样例
class P
{public:P(int a,int b){} // func1P(initializer_list<int> initlist){} // func2
};
//调用
P a(10,20); //调用func1
P b{5,7}; //func2
P c{2,23,62}; //func2
P d={3,5}; //func
上述样例中如果没有
func2
,则对象b d
仍会创建,调用func1
源码
template<class _E>class initializer_list{public:typedef _E value_type;typedef const _E& reference;typedef const _E& const_reference;typedef size_t size_type;typedef const _E* iterator;typedef const _E* const_iterator;private:iterator _M_array;size_type _M_len;//编译器看到{}时会调用该私有构造// The compiler can call a private constructor.constexpr initializer_list(const_iterator __a, size_type __l): _M_array(__a), _M_len(__l) { }public:constexpr initializer_list() noexcept: _M_array(0), _M_len(0) { }// Number of elements.constexpr size_typesize() const noexcept { return _M_len; }// First element.constexpr const_iteratorbegin() const noexcept { return _M_array; }// One past the last element.constexpr const_iteratorend() const noexcept { return begin() + size(); }};
多个同类型的参数max/min通过initializer_list实现
Explicit
作用于含有一个以上实参的构造函数,防止编译器进行隐式类型转换。
C++中nonexplicit one argument ctor
才能做隐式转换。
range-base for
for-loop`本来的格式有三段,现可以简化为两段`for(decl : coll)
vector<int>vec{2,5,547,47,85,373,856,86};
//以前
for(int i = 0;i<vec.size();++i) { cout <<vec[i];}
//现在
for(auto& elem:vec){cout<<elem;}//源码
for(decl:coll)
for(auto _pos=coll.begin();_pos !=coll.end();++_pos){decl = *_pos;}
=default/=delete
如果有定义构造函数(ctor),则编译器不会再提供默认构造函数(default ctor),如果强制加上=default
,可以重新获得并使用default ctor
。主要用于构造函数、拷贝构造、拷贝赋值、析构函数(不写的话,编译器会自动增加的函数)
class Zoo{Zoo(const Zoo&) = delete;Zoo(Zoo&&) = default;Zoo& operator=(const Zoo&) = default;Zoo& operator=(const Zoo&) = delete;
};
=delete
禁用成员函数的使用。删除特殊成员函数提供了一种更简洁的方法来防止编译器生成我们不想要的特殊成员函数。
Alias Template
模板别名。在专门化别名模板时生成的类型不允许直接或间接地使用其自己的类型:
template<T>
using Vec = std::vector<T,MyAlloc<T>>;
Vec<int> coll;
#define
typedef
无法达到相同效果。
#define
会改变模板中的参数,从而形成类似于偏特化的东西
typedef
不接收参数,也无法达到预期效果。
template template parameter
模板模板参数则是模板的参数又是一个模板,如下
template<typename T, template<typename U> typename Container>
class XCls{private:Container<T> c;
};
//调用 tmp为一个模板类
template<typename T>
class Tmp{};
XCls<std::string, Tmp> test1;
//调用容器
XCls<string,vector> test2; //error
//由于vector会有第二参数(构造器),需要指定
template<typename T>
using Vec = std::Vec<T, std::allocator<T>>;
XCls<string,Vec> test2;
注意事项
以下不为模板模板参数
template<typename T, typename Sequence = list<T>>
class stack
{private:Sequence c;
};
stack<int, deque<int>> s2;
这里是容器指定底层容器,虽然使用了模板参数,但两个参数一旦指定前一个,后一个随之确定。
而模板模板参数两个参数之间没有任何关系。
Type Alias
类型别名,类似于typedef
。
typedef void (*func)(int,int); //参数为int,int返回值为void的函数指针
using func = void(*)(int,int);//需要注意 = 左侧为重命名后的
template<typename T>
class Tmp{using value_type = T;//typedef T value_type;
};
Tmp::value_type xxx;
using
- using-directives 命名空间(
using namespace std
)、using-declarations 某个函数(using std::function
) - using-declarations成员函数(using _Class::xxx)功能同上
- type alias 、alias template去替换
noexcept
不抛出异常。C++中的异常处理是在运行时而不是编译时检测的。为了实现运行时检测,编译器创建额外的代码,然而这会妨碍程序优化。
void func() noexcept(true);//满足条件时不抛出异常
void func() noexcept;//一定不抛出异常
override
重写。子类在想要重写父类的虚函数时,声明为override
(防止子类写错,不是重写虚函数则报错)可以显式的在派生类中声明,哪些成员函数需要被重写,如果没被重写,则编译器会报错。如果不小心漏写了虚函数重写的某个苛刻条件,也可通过编译器的报错,快速定位错误
class Base {public:virtual void func(int x);
};
class Derived: public Base {public:virtual void func(unsigned int x) override;//error 非重写virtual void func(int x) override;
};
final
用于修饰类、成员变量和成员函数。
- final修饰的类,不能被继承,其中所有的函数都不能被重写。
- final修饰的成员函数不能被重写。
- final修饰的变量不能更改。
decltype
类型自动推导
- 用来声明返回值类型。
template<typename T1,typename T2>
auto add(T1 x,T2 y)->decltype(x+y); //取决于T1 T2两个类的设计
- 函数模板设计中获取某对象类型
template<typename T>
void func(T obj){typedef typename decltype(obj)::iterator iType;//防止编译不通过
}
- 获取
lambda
表达式的返回值的类型(用来声明)
auto cmp = [](const Person1& p1,const Person2& p2){...};set<Person,decltype(cmp)>Myset(cmp);
Lambda
无名仿函数。允许定义内联函数,用来当成参数、对象使用。是一组功能的定义,可以被定义在表达式里。
声明
[CaptureList] (ParamsList) mutable exception-> ReturnType { FunctionBody }
CaptureList
— 捕获外部变量列表ParamsList
— 形参列表mutable
— 用来说明是否可以修改捕获的变量exception
— 异常设定ReturnType
— 返回类型FunctionBody
— 函数体
省略形式
序号 | 格式 |
---|---|
1 |
[CaptureList ] (ParamsList ) -> ReturnType {FunctionBody }
|
2 |
[CaptureList ] (ParamsList ) {FunctionBody }
|
3 |
[CaptureList ] {FunctionBody }
|
- 格式1声明了
const
类型的表达式,这种类型的表达式不能修改捕获列表中的值。 - 格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型
- 如果
FunctionBody
中存在return
语句,则该Lambda表达式的返回类型由return
语句的返回类型确定。 - 如果
FunctionBody
中没有return
语句,则返回值为void
类型。
- 如果
- 格式3中省略了参数列表,类似普通函数中的无参函数。
参数详解
CaptureList
Lambda表达式与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。
- []、[var]、[&var]表示不捕获、值传递、引用传递
- [=]表示值传递方式捕获所有父作用域的变量
- [&]表示引用传递方式捕捉所有父作用域的变量
ParamsList
除了捕获列表之外,lambda还可以接受输入参数。参数列表是可选的。
mutable
mutable修饰符, 默认情况下Lambda函数总是一个const
函数,mutable
可以取消其常量性。
在使用该修饰符时,参数列表不可省略。
exception
指示 Lambda 表达式不会引发任何异常。
ReturnType
返回类型会自动推导
FunctionBody
可以包含普通方法或函数的主体可以包含的任何内容。
样例
vector<int> myvec{ 3, 2, 5, 7, 3, 2 };
vector<int> lbvec(myvec);//仿函数
bool cmp(int a, int b)
{return a < b;
}
sort(myvec.begin(), myvec.end(), cmp); // Lambda表达式
sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; }); //特例
int id = 0;
auto func = [id]()mutable{cout<<"id = "<< id << endl;++id;//如果没有mutable id不可更改
}
id = 200;
func();
func();
func();
cout <<"id = " << id<< endl;
/*
0
1
2
200
*/
Rvalue reference
右值引用。
通俗来说,可以取地址、有名字的为左值。(在内存中有实际地址的值、可以出现在赋值左侧的值)
右值引用就是对一个右值进行引用的类型。左值引用就是对一个左值进行引用的类型。
为了解决非必要的拷贝。当赋值的右侧为一个右值时,左侧可以在右侧偷出值,而不需要调用构造器。
左值引用
左值引用包括常量左值引用和非常量左值引用。非常量左值引用只能接受左值,不能接受右值;
int &a = 2; // 非常量左值引用 绑定到 右值,编译失败int b = 2; // b 是非常量左值
const int &c = b; // 常量左值引用 绑定到 非常量左值,编译通过const int d = 2; // d 是常量左值
const int &e = d; // 常量左值引用 绑定到 常量左值,编译通过
const int &f =2; // 常量左值引用 绑定到 右值,编译通过
右值引用
右值引用独立于左值和右值。即,右值引用类型的变量可能是左值也可能是右值。
int&& var1 = x;
var1类型为右值引用,但var1本身是左值,因为具名变量都是左值。
T&& 并不一定表示右值,它绑定的类型是未定的,既可能是左值又可能是右值。
template<typename T>
void f(T&& param){}f(10); //param是右值int x = 10;
f(x); //param是左值
/*****************
参数为右值10时
param 被一个右值初始化
那么 param 就是右值当参数为左值 x 时
param 被一个左值初始化
那么 param 就是一个左值
*****************/
std::move()
该函数并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换static_cast<T&&>(Lvalue);
Perfect forwarding
nonperfect forwarding
— 当我们对一个右值调用函数时,右值会调用move
函数,然后调用函数时调用相应的左指的函数。此时即为一个不完美的交付
void process(int& i){ cout<<"process(&)"<<i<<endl;}
void process(int&& i){ cout<<"process(&&)"<<i<<endl;}void forward(int&& i){cout<<"forward(&&)"<<i<<endl;process(i);
}
forward(2);
/*************
首先2是右值 调用相应函数
调用之后在forward(int &&)中
i变为一个左值,调用process(int &)
而理想状态应该调用process(int &&)
故,为一个不完美的交付
**************/
标准库中提供函数std::foward
来实现完美的交付
容器array
没有ctor
,没有dtor
。封装成类的数组。创建时指定大小,不可扩容。
Tuple
元组是将不同类型的元素打包到一个对象中使用,就像pair
适用于成对的元素,但tuple
可以泛化为任意数量的元素。相关函数如下
tuple_size()
— 获取元组中元素的个数tuple_element()
— 访问tuple中指定位置的元素make_tuple()
— 构造包含指定内容的元组get<index>()
— 获取元组中第index个元素
tuple<int,double,string> temp(11,42.2,"xxx");//手动建元组
get<id>(temp);//获取元组temp中的第id个元素
auto temp2 = make_tuple(15,41.3,"sss");//把括号里的一包xxx生成为tuple
cout <<tuple_size<decltype(temp)>::value;//元组中元素个数 3
cout <<tuple_element<0,decltype(temp)>::type;//第0个元素
cout << temp <<endl;//tuple可以直接cout
get<1>(temp) = get<1>(temp2);//取元素赋值
temp = temp2;//直接赋值
int myint;
double mydouble;
string mystring;
tie(myint,mydouble,mystring) = temp;//tie对应赋值
C++2.0一些特性相关推荐
- JDK5.0新特性系列---目录
JDK5.0新特性系列---目录 JDK5.0新特性系列---1.自动装箱和拆箱 JDK5.0新特性系列---2.新的for循环 JDK5.0新特性系列---3.枚举类型 JDK5.0新特性系列--- ...
- [转]C# 2.0新特性与C# 3.5新特性
C# 2.0新特性与C# 3.5新特性 一.C# 2.0 新特性: 1.泛型List<MyObject> obj_list=new List(); obj_list.Add(new MyO ...
- Servlet 3.0 新特性概述
Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若 ...
- Redis 6.0 新特性-多线程连环13问!
来自:码大叔 导读:支持多线程的Redis6.0版本于2020-05-02终于发布了,为什么Redis忽然要支持多线程?如何开启多线程?开启后性能提升效果如何?线程数量该如何设置?开启多线程后会不会有 ...
- WCF4.0新特性体验(3):标准终结点(Standard Endpoints)
今天在WCF4.0新特性体验第3节,我们介绍WCF4.0里的标准终结点概念,也就是Standard Endpoints. WCF4.0提供了那些标准终结点?他们有什么作用?如何使用标准终结点?如何该表 ...
- Servlet 2.0 Servlet 3.0 新特性
概念:透传. Callback 在异步线程中是如何使用的.?? Servlet 2.0 && Servlet 3.0 新特性 Servlet 2.0 && Servle ...
- C#6.0,C#7.0新特性
C#6.0,C#7.0新特性 C#6.0新特性 Auto-Property enhancements(自动属性增强) Read-only auto-properties (真正的只读属性) Auto- ...
- WCF4.0新特性体验(6):路由服务Routing Service(下)
紧接前文WCF4.0新特性体验(5):路由服务Routing Service(上).今天我们介绍WCF4.0消息路由的实现机制,然后会讲解路由服务的实现过程. [4]WCF与路由服务: 其实在介绍WC ...
- 【收藏】C# 2.03.0新特性总结
c#2.0新特性 范型 我们知道通用的数据结构可以采用object存储任何数据类型.使用object问题是: 显示的强制转带来的代码复杂性 换装箱拆箱的性能损失(为什么有性能损失?因为涉及动态内存分配 ...
- 返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API
返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API 原文:返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 ...
最新文章
- iframe几种常用代码片段
- SylixOS与硬件设备连接问题——硬件设备串口、网口连接问题
- C# HashTable的用法总结
- zend studio 函数不提醒 小黄图标 小黄标
- QT的QListIterator类的使用
- TextBox控件怎样赋值化学符号
- Web网络知识:什么是HTTP请求合并?
- 使用Python查看汉诺塔移动详细过程
- ubuntu安装cuda11.2
- protoc 命令 java_protoc 指令介绍
- python——algorithms模块
- XML解析——Java中XML的四种解析方式
- linux硬盘支持fat32,Linux下,挂载windows管理格式的FAT32/NTFS 硬盘
- Web前端开发项目(记忆卡片)
- 捋一捋Python中的List(上)
- 使用eclipse实现阿里云物联网平台数字签名(Signature)(附源代码)
- 传统的人事管理与人力资源管理有什么区别,后者有了什么新突破?
- SD/SDIO/EMMC
- 996程序员入职一年多,同事涨4千他没涨,跟领导提涨薪,回复愣了
- WordPress自定义gravatar头像,缓存Gravatar头像为网站提速