知识点总结(1)

1.extern关键字的作用
extern置于变量或函数前,表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其它模块中寻找其定义,也可以用来进行链接指定。
extern “C” 告诉编译器在编译函数名时要按照C的规则去翻译相应的函数名,而不是C++的,C++的规则在翻译函数名的时候会把函数名变的面目全非(C++支持重载)。C++语言在编译时为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言不会,因此造成链接时找不到对应的函数情况,此时C函数就需要用extern“C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
函数的声明中带有关键字extern仅仅是暗示这个函数可能在别的源文件里定义,没有其他作用,即extern int fun()和int fun()没有明显区别。但在程序中可以取代头文件,如果A模块的变量在头文件中声明并且定义的,B模块想用该变量时,如果包含A的头文件会报错,因为B再一次定义和声明了该变量,连接器链接时就会发现两个变量,这时候B模块不要包含头文件了,用extern,编译器知道这是引用的外部的模块了,不会重复定义,但这样做的坏处是,不包含A的头文件,导致不能使用A模块其他函数了,除非那些函数也用extern修饰。所以尽量只在头文件中声明,然后cpp中定义

extern和static
(1)extern表明该变量在别的地方已经定义过了,在这里要使用那个变量。
(2)static表示静态的变量,分配内存的时候,存储在静态区,不存储在栈上面。
static 作用范围是内部连接的关系, 和extern有点相反.它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern 引用,而static 不可以,只允许对象本身用它. 具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它

(1) test1.h:
    #ifndef TEST1H
    #define TEST1H
    static char g_str[] = "123456"; 
    void fun1();
    #endif

    (2) test1.cpp:
    #include "test1.h"
    void fun1()  {   cout << g_str << endl;  }
    (3) test2.cpp
    #include "test1.h"
    void fun2()  {   cout << g_str << endl;  }

以上两个编译单元可以连接成功, 当你打开test1.obj时,你可以在它里面找到字符串"123456",同时你也可以在test2.obj中找到它们,它们之所以可以连接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。 也许你比较较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址相同,于是你下结论static修饰的变量也可以作用于其他模块,但是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,执行效率更高,当编译器在连接各个编译单元的时候,它会把相同内容的内存只拷贝一份,比如上面的"123456", 位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就只会存在一份了,如果你把上面的代码改成下面的样子,你马上就可以拆穿编译器的谎言

 (1) test1.cpp:
    #include "test1.h"
    void fun1()
    {
        g_str[0] = ''a'';
        cout << g_str << endl;
    }

    (2) test2.cpp
    #include "test1.h"
    void fun2()  {  cout << g_str << endl;  }
    (3) void main()     {
        fun1(); // a23456
        fun2(); // 123456
    }

这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,所以编译器被强行的恢复内存的原貌,在内存中存在了两份拷贝给两个模块中的变量使用。正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!

extern和const
C++中const修饰的全局常量有跟static相同的特性,它们只能作用于本编译模块中,但是const可以与extern连用声明该变量可以作用于其他编译模块中。
当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了

  1. C++函数重载原理
    在1中说extern时,说到了函数重载,所以有必要研究一下。
    重载规则:
    (1)函数名要相同
    (2)参数个数不同,参数的类型不同,参数顺序不同均可构成重载
    (3)只有返回值不同不可以构成重载(因为有的函数虽然有返回值类型,但不参与表达式运算,而作为一条单独的语句)
    重载的底层原理
    是利用name mangling(倾轧(ya)),技术也可以说是命名粉碎规则实现的,通过该技术改变函数名,区分参数不同的同名函数。倾轧技术需要根据参数的顺序和类型来改变命名,所以参数的顺序和类型不同都可以构成重载。原理是用v-c-i-f-l-d表示void char int float long double类型及其引用。例如:
void print(int a); // print_i
void print(char a); // print_c
void print(int a, char b); // print_ic
void print(char a, int b); // print_ci

通过倾轧技术,会把函数名改变成原函数名加下划线加参数类型缩写的形式,实际可能会更复杂,但都能达到区分的目的。
需要注意的是,函数的声明和定义只要有一方使用了倾轧,那么需要保持一致。即,.h文件中声明函数前面加了extern“C”,那么.c中定义时,也要带extern“C”,不倾轧都不倾轧。否则不能通过编译。

  1. C和C++的区别
    设计思想上:
    C++是面向对象的语言,而C是面向过程的结构化编程语言

语法上:

C++具有封装、继承和多态三种特性

C++相比C,增加多许多类型安全的功能,比如强制类型转换、

C++支持范式编程,比如模板类、函数模板等
https://blog.csdn.net/angry_youth/article/details/63683180
https://blog.csdn.net/yeming81/article/details/5637700
4. C++的虚拟内存与内存管理机制
参考下面几篇文章:
https://blog.csdn.net/Jeson_Mei/article/details/52218351
https://blog.csdn.net/qq_40416052/article/details/82493916
https://blog.csdn.net/yeming81/article/details/2046193
https://blog.csdn.net/yeming81/article/details/2046207
https://blog.csdn.net/yeming81/article/details/2047879 虚拟内存
https://blog.csdn.net/yeming81/article/details/2050521
https://blog.csdn.net/yeming81/article/details/2052312
5. static关键字的作用
(1)全局静态变量
在全局变量前加上关键字static,全局变量就定义成一个全局静态变量.

静态存储区,在整个程序运行期间一直存在。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);

作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
(2)局部静态变量
在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。

内存中的位置:静态存储区

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);

作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;

(3)静态函数
在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;

warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;

(4) 类的静态成员
在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员,属于类(被存放在数据段中)。不属于某个具体的对象(具体对象的内存是分配在堆中的)。对多个对象来说,静态数据成员只存储一处,供所有对象共用。类的静态成员变量只能在类外初始化,但如果静态成员变量被const修饰,则可以直接在类内初始化。静态变量通过类来调用,也可以通过具体对象来调用,但非静态变量只能通过具体对象来调用

(5)类的静态函数

静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);
当调用一个对象的非静态成员函数时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数并不属于某一对象(所有对象共有,没有this指针,不能访问本类中非静态成员,有点惨),它与任何对象都无关。可以说静态成员函数的出现就是为了处理静态成员变量的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值