C++ 学习(2) ---- 重要关键字

C++ 中新增关键字

新特性 说明
nullptr 用于指向null 指针
auto for-each 自动推导类型 和区间迭代
static_cast dynamic_cast const_cast reinterpret_cast 类型转换
const constpexpr const 和 const 表达式
new delete delete[ ] 分配和回收内存
explicit 防止函数隐式转换
inline 内联函数

nullptr介绍

nullptr 的出现是为了代替NULL,比如下面的两个重载的函数

void demoFunction(int t) {
    cout << "Run para as int" << endl;
}

void demoFunction(char* t) {
    cout << "Run para as char*" << endl;
}

如果调用demoFunction(NULL),某些编译将NULL定义为(void)0,这就会产生歧义;
如果调用 demoFunction(nullptr) 就一定会调用到 demoFunction(char
t)

auto类型推导,区间迭代

auto 可以推导任意基本类型,还可以作为函数的返回值;但是下面场景不能使用auto

  • 函数的参数不能使用auto,因为需要函数参数实现重载特性
  • auto 不能推导出数组类型
//error auto Add(auto a, auto b)
auto Add(uint16_t a, uint16_t b) {
    return a + b;
}

auto index = 8;
auto str = "HelloWorld";
auto ret = Add(1, 2);
    
cout << "index:" << index << " str: " << str << " ret:" << ret << endl;
//auto array[] = {1,2,3,4} // error auto cannot as array

auto 实现区间迭代可以用于容器类型,数组类型的遍历操作;可以大幅度简化代码
示例代码

    //basic usage
    for (uint8_t i = 0; i < sizeof(array)/sizeof(uint16_t); i++) {
        cout << "array[" << static_cast<int>(i) << "] = " << array[i] << endl;
    }
    for (auto t : array) {
        cout << t << endl;
    }
    for (auto &t : array) {
        cout << t << endl;
    }

const 关键字

const 修饰一般变量,用于表示一个常量值,即该变量成为一个常量

const uint32_t x = 2; 效果和下面等价
uint32_t const x = 2;

const 修饰常数组

uint32_t const a[5] = {1,2,3,4,5}; 效果和下面等价
const uint32_t a[5] = {1,2,3,4,5};

const 修饰对象

表示常对象,定义常对象的时候要初始化,表示对象不能再更新

class demo {
 public:
     demo ();
private:
    uint32_t m;
}

const demo a; //和下面一行等价
demo const a;
const 修饰指针
const int *A  // const 修饰指向的对象,指针A可变,A指向的对象不可变

int const *A  // const 修饰指向的对象,指针A可变,A指向的对象不可变

int *const A; // const 修饰A,指针A不可变,A指向的对象可变 

const int *const A // 指针A 和A指向的对象都是不可变的
const 修饰引用

const int& A; //该引用指向的对象不可更新

修饰函数参数

void Function(const int *A); 表示函数参数指针A指向的对象不可变

修饰函数返回值

表示函数返回值是不可变的,特别应用于返回值是指针或者引用的情形,表示指针和引用指向的对象不可变

const char* formattoString(uint32_t format) {
......
case RGBA:
     return "RGBA";
}

修饰类成员函数

表示不能更改类的属性 只读类的成员的值,不能修改类的成员的值

class ClassName {
public:
    int Fun() const; // 不能更改类的属性 只读类的成员的值,不能修改类的成员的值
}

cast 类型转换

dynamic_cast

dynamic_cast 只能用在类的指针或者引用上,用于

  • 向上转型(派生类转换为基类)
  • 向下转型(基类转换为派生类)
    注意向下转型需要判断转型的有效性,使用dynamic_cast 需要 RTTI (运行时类型识别)的支持

向上转型总是成功的,向下转型(因为基类可以派生出多个子类) ,需要判断转换结果是否成功

注意:dynamic_cast只能用在含有虚函数的类,是在运行期间检查

static_cast
  • 基本类型之间的转换(类型于C语言的强制类型转换)
  • void 指针转换为任意基本类型的指针
  • 向上转型或者向下转型

向上转型(子类指针或者引用转换为基类)总是成功的

向下转型可能存在问题,程序或者了一个 dirty 值,但是没有编译报错

// 定义 Base 为基类,Derive为子类
Base *pB = new Base;
Derive *pD = static_cast<Derive*>(pB);
cout << pD->b << endl;//输出0
cout << pD->d << endl;//输出垃圾值

因为pB 创建时指向的是基类的实例,所以转换成为派生类会出现问题,static_cast并不提供类似dynamic_cast的转型成功校验机制,这可能会造成一个难以察觉的Bug

static_cast 是在编译期间进行检查

reinterpret_cast

reinterpret_cast 是非常底层的操作,直接按照内存地址按照给定的类型做转换,是重新解释二进制的底层操作,本质是一个编译期指令,实际动作可能取决于编译器,虽然功能最强但风险最大,且失去了移植性,基本等同于C语言中的强制类型转换

比如将一个int 类型数组名转换成 int类型指针,static_cast 不允许这种转换,因为数组名可以看成一个数组指针,但是可以用reinterpret_cast实现
使用reinterpret_cast 判断大小端

uint32_t a = 0x12345678;
uint8_t *b = reinterpret_cast<uint8_t *>(&a);
bool LittleEndian = (*b == 0x78) ? true : false;
const_cast

const_cast是一种C++的一个运算符,用于去除符合类型的 const属性,变量本身的const属性是不能去除的,要想修改变量的值,一般是去除指针(或引用)的const属性,再进行间接修改

  • 只能转换指针或者引用,包含this 指针
void cpluscpluscastDemo::constcast_use() {
    {      // convert pointer
        const uint32_t *mptr = new uint32_t(100);
        //*nptr = 200;
        //uint32_t *nptr = p;
        uint32_t * nptr = const_cast<uint32_t*>(mptr);
        *nptr = 200;
        cout << "constcast_use *nptr " << *nptr << endl;
    }

    {    
        const uint32_t a = 1000;
        const uint32_t *mptr = &a;
        uint32_t *nptr = const_cast<uint32_t*>(mptr);
        *nptr = 2000;
        cout << "constcast_use *nptr " << *nptr << endl;
    }

    {     // convert ref
        const uint32_t a = 300;
        const uint32_t &mref = a;
        uint32_t &nref = const_cast<uint32_t&>(mref);
        nref = 400;
        cout << "constcast_use *nptr " << nref << endl;
    }
}

// remove function const 
void cpluscpluscastDemo::constcast_test() const {
    string str = "constcast_test";
    //mstr += str; //compile error

    auto p = const_cast<cpluscpluscastDemo*>(this);
    p->mstr += str; // OK mstr append str
}

new delete 用法

new 可以用于开辟单变量地址空间,返回执行该存储空间的地址,可以给单个变量赋初值;
new 也可以用于开辟数组空间;
要访问new 创建的空间,无法通过变量名进行,只能通过赋值的数组进行

int *a = new int; // 将一个int 类型地址赋值给指针 a
int *a = new int(5); // 同上 同时赋初值为5

int* p = new int[100]; //开辟一个大小为100 的数组空间

可以使用 new 创建对象类型和对象类型的数组,创建对象类型数组,会自动调用对应类型的构造函数

delete 用于释放单个类型的内存空间
delete [ ] 用于释放分配的数组类型的空间

new delete 和 malloc free 区别

  • new 返回指定类型的指针,并且可以自动计算大小;malloc 需要手动计算字节数,返回值需要强制转换为实际类型的指针;
  • malloc 只能分配内存,无法对所得的内存初始化;
  • malloc/free 是 C语言的库函数,new/delete 是C++的运算符,作为运算符可以重载,自定义内存分配策略;
  • new/delete 对于对象类型,可以自动调用构造函数和析构函数,malloc/free 无法做到;

malloc/free new/delete 必须成对使用

explicit 关键字

explicit 关键字的作用是防止类构造函数的隐式自动转换

  • explicit 关键字 只修饰只有一个参数的类构造函数,如果参数大于一个,explicit 是无效的
  • 如果类构造函数有多个参数,除了第一个参数以外都有默认值,explicit 也是有效的

隐式自动转换的例子:

class demoObject {
public:
    inline demoObject(uint32_t x):CoordY(0), mstr("init"){
        CoordX = x;
        cout << "call demoObject uint32_t_x" << endl;
    }

    inline demoObject(const char* str, uint32_t x = 0,uint32_t y = 0) {
        mstr = str;
        CoordX = x;
        CoordY = y;
        cout << "call demoObject const char* str" << endl;
    }

    inline ~demoObject() = default;

    static void dumpdemoObject(demoObject r);

    uint32_t CoordX;
    uint32_t CoordY;
    string mstr;
};


{
    demoObject p1(1); // call demoObject uint32_t_x
    demoObject p2 = 1; // call demoObject uint32_t_x
    demoObject p3 = 'A'; // call demoObject uint32_t_x
}
{
    demoObject::dumpdemoObject(10); // dump demoObject r 10 0 init
}

demoObject p2 = 1这种形式是可行的,这里编译的时候会自动转换为 demoObject p2(1);这里可能会产生不预期的结果

如果在构造函数加上 explicit 修饰,后三种情况都会编译不过,因为禁用了构造函数的隐式转换

tips: 另外说明 demoObject p2 = 1 和 demoObject p2;p2 = 1 的含义是不一致的,前者会调用构造函数的隐式转换,后者会调用赋值运算符重载

inline 关键字

inline关键字是C99标准的型关键字,其作用是将函数展开,把函数的代码复制到每一个调用处。这样调用函数的过程就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作,可以节省时间,也会提高程序的执行速度

内联函数的编程要求:

  • inline 必须和函数定义放在一起才能生效,仅用 inline 用于函数声明不生效
  • inline只适合函数体内代码比较简单的函数使用,不能包含复杂的结构控制语句,例如while、switch,并且内联函数本身不能是直接递归函数(函数内部调用自己的函数)
  • inline 函数仅仅是对编译器的一个建议,最后是否内联,要看编译器的意思
  • inline 函数应该放在头文件中,方便编译器展开

inline 的缺陷:

  • inline 函数省去了函数调用的开销,从而提高函数的执行效率,会导致代码膨胀
  • 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大;类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数,所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,809评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,189评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,290评论 0 359
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,399评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,425评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,116评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,710评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,629评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,155评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,261评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,399评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,068评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,758评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,252评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,381评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,747评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,402评论 2 358

推荐阅读更多精彩内容