现代c++中被弃用的语法特性
-
不再允许字符串字面常量赋值给一个char *,只能使用const char * 类型的变量来接,或者使用auto关键字声明的变量
-
auto_ptr被弃用,使用unique_ptr
-
register关键字被弃用,可以使用,但是不具备任何意义
-
bool类型的++操作被弃用
-
如果一个类有析构函数,为其生成拷贝构造函数和拷贝赋值运算符的特性被弃用了
-
c语言风格的强制类型转换被弃用,应该使用static_cast,reinterpret_cast,等等的代替
现代c++新特性
-
nullptr:
nullptr出现的目的是为了代替NULL,在以前的编译器中,传统c++会把NULL,0,视为同一种东西,有些编译器也会讲NULL定义为((void*)0)nullptr的出现是为了区分0和空指针
-
constexpr:
c++本身已经具备了常量表达式的概念,比如1+2,3*4这种表达式总是会产生相同的结果,并且不会产生任何副作用,c++11提供了constexpr让用户显示地声明函数或者对象构造函数在编译器会成为常量表达式,这个关键字明确地告诉编译器应该去验证表达式在编译器就应该是一个常量表达式。
从c++14开始,constexpr函数可以在内部使用局部变量,循环和分支等简单语句。或者在递归中使用constexpr
#include <iostream>
using namespace std;
constexpr int expX = 3*4;
int array[expX];
int main()
{
cout << "Hello World!" << endl;
return 0;
}
constexpr在递归中的使用
#include <iostream>
using namespace std;
#if 0
constexpr int expX = 3*4;
int array[expX];
#endif
constexpr int fibonacci(const int number){
return number == 1 || number==2?1:fibonacci(number-1)+fibonacci(number-2);
}
static int a = fibonacci(10);
int main()
{
cout << "Hello World!" << endl;
cout<<a<<endl;
return 0;
}
在传统c++中,要在if/switch中使用临时变量时只能提前创建好之后再在内部使用,新特性中,支持在if/switch语句中直接创建临时变量做判断或逻辑处理
c++11/14中新增了std::tuple容器用于构造一个元素,进而囊括多个返回值,但缺陷是并没有提供一个简单的方法从容器中拿到并定义元组中的元素,虽然可以使用std::tie对元组进行拆包,但是必须提前知道元素包含多少个对象,各个对象是什么类型,非常麻烦。但c++17完善了这一设定,有如下代码
#include <iostream>
#include <tuple>
using namespace std;
//定义一个元组
typedef tuple<int,double,string> myTup;
myTup functionX(){
return make_tuple(10,20.125,"ismyTup");
}
int main()
{
cout << "Hello World!" << endl;
auto [x,y,z] = functionX();
cout<<x<<y<<z<<endl;
return 0;
}
程序执行成功的前提是编译器支持c++17 并且在编译时需要指定编译标准
c++11引入了auto和decltype,这两个关键字实现了类型推导,让编译器来推导变量类型,某种意义上提供了无需操心变量类型的使用习惯
c语言中的auto只是保存局部变量,这和早期的c++中的auto几乎一样,使用auto一般可以接收,定义,声明一个复杂类型的变量的值
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
map<int,string> txt;
txt.insert(make_pair(1,"yihao"));
txt.insert(make_pair(2,"erhao"));
txt.insert(make_pair(3,"sanhao"));
txt.insert(make_pair(4,"sihao"));
txt.insert(make_pair(5,"wuhao"));
for(auto k = txt.begin();k!=txt.end();k++){
cout<<k->first<<endl;
cout<<k->second<<endl;
}
return 0;
}
使用if constexpr可以让程序在编译阶段,就判断出if的分支,使得程序运行效率更高,将constexpr这个关键字引入到if语句中,允许在代码中声明常量表达式的判断条件
c++11中引入的区间for迭代,让for循环的迭代工作量减小不少,看如下代码
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
map<int,string> txt;
txt.insert(make_pair(1,"yihao"));
txt.insert(make_pair(2,"erhao"));
txt.insert(make_pair(3,"sanhao"));
txt.insert(make_pair(4,"sihao"));
txt.insert(make_pair(5,"wuhao"));
//for循环中,使用引用访问,可以读写map中的内容
//如果改为for(auto k:txt)那么txt中的内容将只能度,不能写
for(auto &k:txt){
cout<<k.first<<endl;
cout<<k.second<<endl;
}
return 0;
}
c++11引入了委托构造的概念,委托构造使得在写构造函数的时候,可以委托类的另一个构造函数来实现当前构造函数
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
class Base{
public:
Base(){
a = 10;
}
Base(int h):Base(){
b = h;
}
public:
int a,b;
};
int main()
{
cout << "Hello World!" << endl;
Base b(5);
cout<<b.a<<"******"<<b.b<<endl;
return 0;
}
传统c++中如果需要继承构造,是需要将构造函数一一传递的,使用继承构造可以避免这种做法
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
class Base{
public:
Base(){
a = 10;
}
Base(int h):Base(){
b = h;
}
public:
int a,b;
};
class subBase:public Base{
public:
using Base::Base;
public:
};
int main()
{
cout << "Hello World!" << endl;
subBase b(3);
cout<<b.a<<"******"<<b.b<<endl;
return 0;
}
使用override关键字,声明一个函数是需要被子类重载的,因为在传统c++中很容易发生意外重载虚函数的情况,例如:子类中,并不是想重载虚函数,只是写了一个和父类中同名的函数,另一个情形是基类中的虚函数假设被删除后,子类拥有的旧的函数就退化为了一个普通函数,这将导致程序不能很好地执行逻辑
重载时,引入override关键字,关键字将显示地告知编译器,去基类中查找是否存在一个同名的虚函数需要被重载,如果没找到,则会编译不过
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
class Base{
public:
Base(){
cout<<"parent~~~~"<<endl;
}
virtual void overTest(){
}
};
class subClass:public Base
{
public:
subClass(){
}
public:
void overTest() override
{
}
};
int main()
{
cout << "Hello World!" << endl;
return 0;
}
-
final可以告知当前函数不能被继续继承
-
使用delete 可以显示地表明拒绝使用编译器提供的默认构造函数
-
强类型枚举
在传统的c++开发中,枚举都被视作整数,假设枚举类型在表示一种状态是,不小心会让枚举与整数类型的比较产生错误的逻辑,c++11 中引入了枚举类,规定枚举的类型,这时枚举就无法和整形进行比较,使用enum class的语法进行声明
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
enum class new_enum:unsigned int{
value1,
value2,
value3,
value4,
};
new_enum vx1 = new_enum::value1;
//错误,无法实现两个不同类型的比较,并且无法进行隐式类型转换,但是在传统c++中的定义这两个值是可以比较的
if(vx1 == 1){
}
return 0;
}
lambda表达式是现代c++中最重要的特性之一,而lambda表达式实际上就是提供了一个匿名函数的特性,匿名函数的使用场景是需要一个函数,但是又不想费力去命名一个函数的情况下去使用,lambda表达式的基础语法如下:
[捕获列表](参数列表)mutable(可选) 异常属性 ->返回值类型{
//函数体
}
捕获列表,一般分为值捕获(=)和引用捕获(&)
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
int x1=10,y1= 20;
auto number = [x1,y1](int k = 10)->int{
return x1+y1+k;
};
int x = number(20);
cout<<x<<endl;
return 0;
}
传统c++语法中一直被诟病的是在代码中会大量进行值拷贝,严重影响了程序运行的效率,在c++11中引入了move语义来解决这一问题
左值的概念:左值表示在表达式执行完之后理论上还可以被寻址的值
右值的概念:当表达式执行完后,不能被寻址的称之为有值
将亡值:最常见的就是函数的返回值,在return之后,函数结束之前的变量可以称之为将亡值
move就是在底层让将亡值可以寻址,被移动,从而消除变量之间的拷贝的问题
右值引用与左值引用
如果需要拿到一个将亡值,则需要使用右值引用,T && 右值引用的声明让这个临时值的声明周期得以延长,c++11提供了一个std::move()方法将一个左值参数无条件转换为右值,
#include <iostream>
#include <tuple>
#include <map>
using namespace std;
//将函数返回值声明为右值引用
int && functionX(int x,int y){
return move(x+y);
//使用move转换
}
int main()
{
cout << "Hello World!" << endl;
int z = 50;
int && b = move(z); //使用move将一个左值转换为一个右值
cout<<functionX(20,50);
return 0;
}
RAII与引用计数
引用计数设计的目的设计为了防止内存泄露产生的,基本的想法是对动态内存分配的对象。进行引用计数,每当增加一次对同一个对象的引用,那么引用对象的引用计数就会增加一次,每删除一次对象,引用计数就会减一,当一个引用对象的引用计数减为零时,就自动删除指向的堆内存。在传统c++中,总是需要手动释放资源,这很容易造成内存泄露。
std::shared_ptr:是一种智能指针,他能够记录多少个shared_ptr共同指向同一对象,从而消除显示地调用delete,但是使用shared_ptr还是会显示地调new,有new,而没有delete从某种程度上来说,代码是不对称的,因此一般使用std::make_shared来消除显示地调用new,有如下代码
#include <iostream>
#include <tuple>
#include <map>
#include <memory>
using namespace std;
void operationX(shared_ptr<int> point){
(*point)++;
cout<<"referCount"<<point.use_count()<<endl;
}
int main()
{
cout << "Hello World!" << endl;
shared_ptr point = make_shared<int>(50); //创建一个int类型的智能指针
cout<<"referCount"<<point.use_count()<<endl;
(*point)++;
cout<<"referCount"<<point.use_count()<<endl;
operationX(point);
cout<<"referCount"<<point.use_count()<<endl;
auto pointX = point;
cout<<"referCount"<<point.use_count()<<endl;
cout<<"referCount"<<pointX.use_count()<<endl;
return 0;
}
当退出main函数时,引用计数会减为零
std::unique_ptr:也是智能指针的一种,但是和shared_ptr区别,unique_ptr是独占式的,也就是不可复制的,除了使用move将其转移给其他的unique_ptr
RTTI概念解析:RTTI即为运行时类型识别,程序能够使用基类的指针或者引用来检查这些指针或引用指向的是哪个派生类对象的实际类型
typeid可以返回指针和引用的实际类型
dynamic_cast操作符,将基类类型的指针安全地转为派生类类型的指针或者引用。