C++ 重点知识梳理 (一) --------- 重点关键字及其用法

该文章的要感谢博主:csdn_chai

本文总结一下C++面试时常遇到的问题。C++面试中,主要涉及的考点有

  • 关键字极其用法,常考的关键字有const, sizeof, typedef, inline, static, extern, new, delete等等
  • 语法问题
  • 类型转换
  • 指针以及指针和引用的区别
  • 面向对象的相关问题,如虚函数机制等
  • 泛型编程的相关问题,如模板和函数的区别等
  • 内存管理,如字节对齐(内存对齐)、动态内存管理、内存泄漏等
  • 编译和链接
  • 实现函数和类

 

零、序章

0.1 C++与C的对比

  1. C++有三种编程方式:过程性,面向对象,泛型编程。
  2. C++函数符号由 函数名+参数类型 组成,C只有函数名。所以,C没有函数重载的概念。
  3. C++ 在 C的基础上增加了封装、继承、多态的概念
  4. C++增加了泛型编程
  5. C++增加了异常处理,C没有异常处理
  6. C++增加了bool型
  7. C++允许无名的函数形参(如果这个形参没有被用到的话)
  8. C允许main函数调用自己
  9. C++支持默认参数,C不支持
  10. C语言中,局部变量必须在函数开头定义,不允许类似for(int a = 0; ;;)这种定义方法。
  11. C++增加了引用
  12. C允许变长数组,C++不允许
  13. C中函数原型可选,C++中在调用之前必须声明函数原型
  14. C++增加了STL标准模板库来支持数据结构和算法

 

一、重要的关键字极其用法

1.1 const 

主要用法

C++ 的const关键字的作用有很多,几乎无处不在,面试中往往会问“说一说const有哪些用法”。下面是一些常见的const用法的总结:

 

 

除此以外,const的用法还有:

  • const引用可以引用右值,如const int& a = 1; 

注:

  1. const 成员方法本质上是使得this指针是指向const对象的指针,所以在const方法内,
  2. const 成员函数可以被非const和const对象调用,而const对象只能调用const 成员函数。原因得从C++底层找,C++方法调用时,会传一个隐形的this参数(本质上是对象的地址,形参名为this)进去,所有成员方法的第一个参数是this隐形指针。const成员函数的this指针是指向const对象的const指针,当非const对象调用const方法时,实参this指针的类型是非const对象的const指针,赋给const对象的const指针没有问题;但是如果const对象调用非const方法,此时实参this指针是指向const对象的const指针,无法赋给非const对象的const指针,所以无法调用。注意this实参是放在ecx寄存器中,而不是压入栈中,这是this的特殊之处。在类的非成员函数中如果要用到类的成员变量,就可以通过访问ecx寄存器来得到指向对象的this指针,然后再通过this指针加上成员变量的偏移量来找到相应的成员变量。文章来源http://blog.csdn.net/starlee/article/details/2062586/
  3. const 指针、指向const的指针和指向const的const指针,涉及到const的特性“const左效、最左右效”
  4. const 全局变量有内部链接性,即不同的文件可以定义不同的同名const全局变量,使用extern定义可以消除内部链接性,称为类似全局变量,如extern const int a = 10.另一个文件使用extern const int a; 来引用。而且编译器会在编译时,将const变量替换为它的值,类似define那样。

 

const 常量和define 的区别

  1. const常量有数据类型,而宏定义没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意想不到的错误(边际效应)。
  2. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏定义进行调试。
  3. 在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
  4. 内存空间的分配上。define进行宏定义的时候,不会分配内存空间,编译时会在main函数里进行替换,只是单纯的替换,不会进行任何检查,比如类型,语句结构等,即宏定义常量只是纯粹的置放关系,如#define null 0;编译器在遇到null时总是用0代替null它没有数据类型.而const定义的常量具有数据类型,定义数据类型的常量便于编译器进行数据检查,使程序可能出现错误进行排查,所以const与define之间的区别在于const定义常量排除了程序之间的不安全性.
  5. const常量存在于程序的数据段,#define常量存在于程序的代码段
  6. const常量存在“常量折叠”,在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表,可以算作一种编译优化。因为编译器在优化的过程中,会把碰见的const全部以内容替换掉,类似宏。

1.2 sizeof

  1. sizeof关键字不会计算表达式的值,而只会根据类型推断大小。
  2. sizeof() 的括号可以省略, 如 sizeof a ; 
  3. 类A的大小是 所有非静态成员变量大小之和+虚函数指针大小

1.3 static 

static的用法有:

(1)声明静态全局变量,如static int a; 静态全局变量的特点:

  • 该变量在全局数据区分配内存; 
  • 未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化); 
  • 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的; 

(2)声明静态局部变量,即在函数内部声明的,静态局部变量的特点:

  • 该变量在全局数据区分配内存; 
  • 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化; 
  • 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0; 
  • 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

(3)声明静态函数,限定函数的局部访问性,仅在文件内部可见

(4)类的静态数据成员,与全局变量相比,静态数据成员的好处有:

  • 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性; 
  • 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

(5)类的静态方法

1.4 typedef 

typedef 用来定义新的类型,类似的还有#define 和 using (C++11) (应该尽可能用using ,比如 using AAA = int64_t; )

与宏定义的对比

  1. #define 在预处理阶段进行简单替换,不做类型检查; typedef在编译阶段处理,在作用域内给类型一个别名。
  2. typedef 是一个语句,结尾有分号;#define是一个宏指令,结尾没有分号
  3. typedef int* pInt; 和 #define pInt int* 不等价,前者定义 pInt a, b;会定义两个指针,后者是一个指针,一个int。

1.5 inline

inline用来向编译器请求声明为内联函数,编译器有权拒绝。

与宏函数的对比

  1. 内联函数在运行时可调试,而宏定义不可以;
  2. 编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
  3. 内联函数可以访问类的成员变量,宏定义则不能;
  4. 在类中声明同时定义的成员函数,自动转化为内联函数
  5. 宏只是预定义的函数,在编译阶段不进行类型安全性检查,在编译的时候将对应函数用宏命令替换。对程序性能无影响

不能声明为inline的函数

  1. 包含了递归、循环等结构的函数一般不会被内联。
  2. 虚拟函数一般不会内联,但是如果编译器能在编译时确定具体的调用函数,那么仍然会就地展开该函数。
  3. 如果通过函数指针调用内联函数,那么该函数将不会内联而是通过call进行调用。
  4. 构造和析构函数一般会生成大量代码,因此一般也不适合内联。
  5. 如果内联函数调用了其他函数也不会被内联。

1.6 static const \ const \ static 

1. static const 
static const 数据成员可以在类内初始化 也可以在类外,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化
2. static
static数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
3. const
const数据成员只能在构造函数的初始化列表中初始化;

1.7 explicit 

explicit禁止了隐式转换类型,用来修饰构造函数。原则上应该在所有的构造函数前加explicit关键字,当你有心利用隐式转换的时候再去解除explicit,这样可以大大减少错误的发生。如果一个构造函数 Foo(int) ;则下面的语句是合法的:

Foo f; 

f = 12; // 发生了隐式转换,先调用Foo(int)用12构建了一个临时对象,然后调用赋值运算符复制到 f 中

如果给构造函数加了explicit,即 explicit Foo(int);就只能进行显示转换,无法进行隐式转换了:

f = 12; // 非法,隐式转换

f = Foo(12); // 合法,显示转换

f = (Foo)12;//合法,显示转换,C风格

1.8 extern 

extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值