C++基础:(1)C++对C的加强

C++对C的加强

1. C++命名空间基本常识

所谓namespace,是指标识符的各种可见范围。C++标准库中的所有标识符都被定义于一个名为std的namespace中。

  1. 文件<iostream> 和 <iostream.h>格式不一样,前者没有后缀,实际上,在你的编译器include文件夹中可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀为.h的头文件C++标准已经明确提出不支持了, 早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,C++标准为了和c区别开,也为了正确使用命名空间,规定头文件不使用带后缀.h。因此:
  • 1.1 当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;
  • 1.2 当使用<iostream>时,该头文件没有定义全局命名空间,必须使用namespace std; 这样债能正确使用cout;
  1. 由于namespace的概念,使用C++标准库的任何标识符时,可以有以下三种选择:
  • 2.1 直接指定标识符。例如std::ostream 而不是ostream。完整语句如下:
    std::cout << std:hex << 3.4 << std::endl;
  • 2.2 使用关键字。using std::cout; using std::endl; using std::cin; 以上程序可以写成
    using std::cout;
    using std::endl;
    using std::cin;

    cout << std::hex << 3.4 << endl;
  • 2.3 最方便就是using namespace std; 例如:using namespace std; 这样命名空间std内定义的所有标志符都有效(曝光)。就好像他们被声明为全局变量一样。那么以上语句可以如下写:
    using namespace std;
    cout << hex << 3.4 <<endl;

因为标准库非常庞大,所以程序员在选择的类的名称或者函数名时,就有可能和标准库的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都放在名字空间std中。

但是,这样又会带来一个新问题:无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。所以就有了<iostream.h> 和 <iostream>等等这样的文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。 命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"。

总之,标准C++引入关键字namespace(命名空间/名字空间/名称空间/名域),可以更好地控制标识符的作用域。

既然提高的命名空间这个词,不妨把C语言的命名空间和C++的命名空间两者做一对比,就会更容易接受C++新标准这么指定使用规则的缘由:

C里面的命名空间:

  • 在C语言中只有一个全局作用域;
  • C语言中所有的全局标识符共享同一个作用域;
  • 标识符之间可能发生冲突。

C++提出了命名空间的概念:

  • 命名空间将全局作用域分成不同的部分;
  • 不同命名空间的标识符可以同名而不会发生冲突;
  • 命名空间可以相互嵌套;
  • 全局作用域也叫默认命名空间。

案例代码:

#include "iostream"

// 文件iostream中没有引入标准的std,所以需要我们程序员手工写
using namespace std;

namespace namespaceA
{
    int a = 10;
}

namespace namespaceB
{
    int a = 20;
    // namespace 也支持嵌套
    namespace namespaceC
    {
        struct Teacher
        {
            char name[32];
            int age;
        } ;
    }
}

int main()
{
    cout << "namespace test" << endl; 

    /* using namespace namespaceA; */
    cout << namespaceA::a << endl;
    cout << namespaceB::a << endl;

    using namespaceB::namespaceC::Teacher;
    Teacher t1;
    t1.age = 23;
}

2.“实用性”加强

C语言的变量都必须在作用于的开始位置定义,但是C++更强调语言“实用性”,所有的变量都可以在需要的时候定义。

3. register关键字增强

在C语言中,

  • 1)register关键字请求“编译器”将局部变量存储于寄存器中;
  • 2)C语言中无法获得register变量的地址;
    在C++语言中, 1)C++编译器有自己的优化方式,即使程序员不使用register也可能做优化, 2)C++中可以获得register变量的地址。
  • PS: 所谓的代码优化,就是如果变量出现在循环中被重复的使用,那么编译器在处理代码时就会把变量的存放在寄存器中。

早期的计算机比较慢,编译器不会对代码进行优化,所以使用register来做一个人为地补充。

C++编译器除了有自己的优化代码的方式,在发现程序中需要取register变量的地址的时候,register对变量的声明变得无效。

#include "iostream"
using namespace std;

int main()
{
    int nRet = 0;
    register int a = 100;

    printf("&a = %d\n", &a);
    // 上面的语句在C语言中无法编译通过,但是C++可以变异成功

    return nRet;
}

4. 变量检测增强

C语言里面的一个“灰色地带”:重复定义多个同名的全局变量是合法的;

C++中编译器不允许定义多个同名的全局变量,会直接拒绝这种二义性的做法。

int g_a;
int g_a = 100;

void main()
{
    printf("hello world...\n");
}

5.struct类型增强

C语言不认为预先定义的struct是一种数据类型,所以我使用typedef来自己定义一个数据类型。

C++中就把struct关键字定义的struct直接增强为一种数据类型;

另外还加强的一点是,struct和class一样,可以对自己的元素设定访问权限:public, protected, private。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Teacher
{
    char name[64];
    int age;
};



struct Student
{
    char name[64];
    int id;
};

// advTeacher展示了增强的struct和class的相似性,区别以后再表
struct advTecher
{
public:
    char name[64];
    int  id;

protected:
    int class;

private:
    char age;
    
};

int main()
{
    Teacher t1;         //编译器通过
    Student s1;         //编译器报错
    struct Teacher s1;  //这样编译器才通过
}

6. 三目运算符增强

在C语言中,运算符的结果本质上就是一个数值,它不是变量,运算符表达式的结果直接保存在寄存器中。如果程序员试图把它当做左值来赋值操作,编译器会报错;
C++中运算符的结果本质上被增强成为一个变量,它保存在内存中,是可以当做左值被修改数值的。

那么这是如何从C语言增强的?
其实就是C++的设计者们改变了运算符的返回对象,把C语言中只是返回一个数值改变为,返回一个变量的地址,代码如下所示:

int a = 10, b = 20;

(a < b ? a : b)
// 改为
(a < b ? &a : &b)

这样一来,尽管运算符返回的仍然是一个数值,但是这个数值是一个内存地址,我们想把它当做左值来进行赋值操作,只需要在地址的前面加上*取地址符即可。

*(a < b ? &a : &b) = 50;

这句话在C语言里面也是可以通过编译并且能够成功执行的。C++的增强实质就是C++编译器帮助程序员们完成了这个操作。
而当运算符是右值的时候,就保持C语言中的处理方式。

7. 对关键字const加强

为了尽可能搞清楚这个话题,我把之前的C语言笔记直接搬回来:

辨析一下const修饰的位置:

const int a;
int const a;

上面这两句话的作用是相同的。

const int *a;
int const *a;

这两句话句话也是同样的作用:指针指向地址的内容不能更改,指针指向的地址可以更改。

int *const a;

const修饰的是a:a指向的地址不可以更改,但是地址里面的内容可以更改。
总结了以下规律:

    1. 只看const后面的内容,const 后面是个a,a本身是一个指针变量,所以就是指针的值不能改了;
    1. const后面是*a 或int *a,就是说*a不能改,即a指向的内存的内容不能更改。
int main()
{
    const int a;    // 整型变量a的值不可修改
    int const b;    // 同上

    const char *c;  // 这样的代码按照“从右往左”的顺序开始理解即可避免概念混淆
    char *const d;  // const char *c = const (char *c): char *c 指向的是一个存储char类型的值的内存,
                    // 再用const修饰这个内存地址,意思就是:char *c 指向的内存地址不可以被更改,
                    // 但是指针c可以指向其他的内存地址,指向了新的内存地址后,新的内存的内容也就不能更改。
                    // 同理,char *const d = char (*const d): *d是一个指针变量(类型不明,指向不明,
                    // 但是已经为d分配了内存空间)。那么就是d的指向不能改变,d只能一直指向一个固定
                    // 的内存空间,再用char 来修饰说明,这个d指向的内存空间存储char 类型的值,
                    // 这个内存空间存储的值当然可以修改的 
    
    const char *const e;     // 同理,const char *const e = {const [char *(const e)]}: 
                             // e的值不变,e是一个指针,指针e指向一个char类型变量,
                             // 指针e指向的char变量是不能更改的。
}

下面直接说这个话题的结论:
const关键字在C语言编译器器中并不能真正起到锁定变量值的作用,因为我们只要使用一个变量同类型的指针变量p,用p即可直接修改变量的值;*
但是在C++编译器中真正做到了锁定变量的值,同样的代码,使用p来修改变量值,然后打印结果,发现变量的值不变。*

void main()
{
    const int a = 10;
    int *p = NULL;

    p = (int *)&a;
    *p = 9999;
    
    printf("a: %d \n", a);  
//  C语言编译器处理后运行结果:a: 9999
//  C++编译器处理后运行结果:a: 10
}

下面我们来把C++编译器的工作解释一下:
在使用了const关键字修饰变量后,C++编译器会把变量存放在“符号表”中,符号表里面存储的都是键值对:key-value。
注意:这里的“符号表”是和之前的“内存四区”是两个概念,不要拿来试图相互说明。
但是在代码

p = (int *)&a;

中执行的是什么操作呢?编译器会再分配一个新的空间来存放当前a的值,所以p获取的内存地址和a并没有关系,所以C++编译器把const修饰的变量真正变成了一个常量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • C++入门基础 namespace专题讲座 namespace概念 所谓namespace,是指标识符的各种可见范...
    蔡俊宇阅读 776评论 0 2
  • 一、 为什么需要命名空间(问题提出) 命名空间是ANSIC++引入的可以由用户命名的作用域,用来处理程序中 常见的...
    IvanRunning阅读 2,346评论 0 2
  • 重新系统学习下C++;但是还是少了好多知识点;socket;unix;stl;boost等; C++ 教程 | 菜...
    kakukeme阅读 19,784评论 0 50
  • 第一章 计算机与C++编程简介 C++程序6个阶段编程 ->预处理->编译->连接->装入->执行1.程序在编译器...
    rogertan30阅读 3,667评论 0 1
  • 我的朋友小英姐气鼓鼓地从店门口走过来,把装手机的小包包往茶桌上一摔,大巴鬼大巴鬼地说开了…… 她骂人大巴鬼就是...
    狂小烹阅读 2,408评论 47 34