现代C++中的语法特性

现代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;

}

  • if/switch变量声明强化
在传统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 并且在编译时需要指定编译标准
  • auto自动类型推导/decltype类型推断
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的分支,使得程序运行效率更高,将constexpr这个关键字引入到if语句中,允许在代码中声明常量表达式的判断条件
  • 区间for迭代
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;
}
  • 继承构造之使用using关键字
传统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
使用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表达式
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;
}
  • 右值引用与move语义
传统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操作符,将基类类型的指针安全地转为派生类类型的指针或者引用。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容