0%

C++八股(1)

C++八股文

#define和const的区别

  1. 类型和安全检查不同
    1. 宏定义是字符替换,没有数据类型的区别,并且这种替换没有类型安全检查,可能产生边际效应等错误;
    2. const是常量的声明,有类型区别,在编译阶段会进行类型的检查
  2. 编译器处理不同
    1. 宏定义是一个“编译时”概念,在预处理阶段展开,因此不能对宏定义进行调试
    2. const常量是一个“运行时”的概念,在程序运行使用,相当于一个只读数据
  3. 存储方式不同
    1. 宏定义是直接替换,不会分配内存,存储在程序的代码段中
    2. const常量需要进行内存分配,存储在程序的数据段中
  4. 定义域不同
    在函数1内使用#define N 12const int n=12,在函数2内可以使用N,不可以使用n
  5. 是否可以取消定义
    1. 宏定义可以通过#undef来使之前的宏定义失效,取消后可以重新定义
      2.const常量定义之后在定义域内永久有效
  6. 作为函数参数
    1. 宏定义不能作为参数传递给函数
    2. const常量可以在函数的参数列表中出现

static的作用

  1. 修饰普通变量:修改变量的存储区域和生命周期,存储在静态区,有初始值就用初始值初始化,没有的话就用默认值初始化:
    1. 全局变量:只能在本文件内访问,即使是extern声明也不可以
    2. 局部变量:离开作用域,内存不会释放,但不能访问,只有再次进入作用域才可访问,并且值不变。在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化。
  2. 修饰普通函数:只在本文件可以使用,防止与他人的命名空间中的同名函数冲突
  3. 修饰成员变量:使得该类的所有实例只保存一个该变量,并且不用生成对象就能通过类名::变量名访问
  4. 修饰成员函数:不需要生成对象就能使用函数,但是不能访问非静态成员

inline内联函数

  1. 特征:
    1. 把内联函数里的代码复制到内联函数调用处
    2. 相当于宏,但是比宏多了类型检查,真正具有函数特性
    3. 内联相当于对编译器的一个建议,具体是否内联看编译器选择。编译器一般不内联包含循环、递归等复杂操作的内联函数
    4. 类中定义的函数,除了虚函数以外都会自动隐式地当成内联函数
  2. 优点:
    1. 内联函数和宏函数一样在调用时展开,省去了函数调用的开销
    2. 内联函数比宏函数多了类型检查
    3. 内联函数在运行时可调试,而宏定义不行
  3. 缺点:
    1. 代码膨胀,每一次内联函数调用都要复制代码,消耗内存空间
    2. 是否内联对程序员来说不可控,内联是建议,具体是否内联看编译器决定
  4. Q:虚函数可以是内联函数吗?
    内联是可以修饰虚函数的,但是当虚函数表现为多态时不能被内联:内联是在编译期建议编译器内联,而虚函数的多态在运行时才能动态确定调用哪个函数,所以虚函数表现为多态时不能内联。

volatile关键字

  • 用来修饰变量,表示该变量可以被某些编译器未知的因素更改,比如操作系统、硬件或者其他线程。
  • 遇到这个关键字声明的变量,编译器对该变量访问的代码不进行优化:系统总是重新从他所在的内存读取数据,即使它前面的指令刚刚读取过,而且读取的数据立刻被保存。
    • 如果是被优化的做法:编译器发现两次读取数据的代码没有对该数据进行操作,会自动使用上次读的数据

assert()

  • 断言——是宏而不是函数,定义在头文件<assert.h>(C)<cassert>(C++)
  • 作用:如果条件返回错误,则终止程序执行
1
2
3
4
#define NDEBUG          // 加上这行,则 assert 不可用
#include <assert.h>

assert( p != NULL ); // assert 不可用

pragma pack(n)

设定结构体、联合体以及类成员变量以n字节方式对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#pragma pack(push)  // 保存对齐状态
#pragma pack(4) // 设定为 4 字节对齐

struct test1
{
char m1;
double m4;
int m3;
};

#pragma pack(pop) // 恢复对齐状态
struct test2
{
char m1;
double m4;
int m3;
};

int main()
{
cout<<sizeof(test1)<<endl; //16=4+8+4
cout<<sizeof(test2)<<endl; //24=8+8+8
}

C++中的class和struct

  • struct更适合看成一个数据结构的实现体=各种数据类型的集合
  • class更适合看成一个对象的实现体=一个对象的方法和属性的集合
    区别:
  1. 默认的访问属性不同:struct默认是public,class默认是private
  2. 默认的继承方式不同:struct默认的继承方式是public,class默认是private

union

union是一种节省空间的特殊的类,可有多个数据成员,但是在任意时刻只有一个数据成员可以有值,当某个成员变量被赋值后其他成员变为未定义状态
特点:

  1. 默认访问控制符为public
  2. 可以有构造函数和析构函数
  3. 不能有引用类型的成员变量
  4. 不能有虚函数
  5. 不能继承自其他类,不能作为基类
  6. 联合体的大小=最大的那个成员变量的大小

explicit

——C++新标准出现的一个关键字
explicit作用:表明该构造函数是显式的,而非隐式的,不能进行隐式转换
相对应的另一个是implicit,类构造函数默认情况即声明为implicit,表示这个类是可以隐式构造的,但是implicit不是关键字,只能说是编译器的一种修饰,不能自己写上

隐式:编译器完成的转换,int a=1;float b=3;float sum=a+b这里编译器将a隐式地转换为了float
显式:用户完成的转换,float a=1;float b=3;int sum=(int)a+(int)b;这里a和b被显式的转化为了float

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A{
public:
// explicit
A(int a,int b):a(a),b(b){};
int b;
int a;
};
void f1(A a){
cout<<a.a<<" "<<a.b<<endl;
}

int main(){
A tmp1=A(1,2); // 显示构造
A tmp2={10,20}; // 隐式构造,如果加上explicit禁止隐式构造
f1(tmp1);
f1(tmp2);
}

friend友元类和友元函数

在C++中,一个类中可以有public protected private三种属性的成员,通过对象可以访问public成员,只有本类中的函数可以本类的private成员。
一种例外情况:友元,借助友元,可以使得其他类中的成员函数以及全局范围内的函数能够访问当前类的private成员

友元函数

在当前类中定义的,不属于当前类的成员函数也可以在类中声明,但要在前面加friend关键字,这样就构成了友元函数。
友元函数可以是不属于任何类的非成员函数和,也可以是其他类的成员函数

  1. 将非成员函数声明为友元函数

    1
    2
    3
    4
    5
    6
    class Student{
    private:
    int name;
    public:
    friend void show(Student *stu);
    }

    注意:友元函数不同于成员函数,不能直接访问类的成员,必须借助对象
    因为成员函数在调用时会隐式地增加this指针,指向调用它的对象,从而使用该对象的成员;而show()是非成员函数,没有this指针,所以编译器不知道使用哪个对象的成员,所以必须通过参数传递对象,并通过对象访问成员。

  2. 将其他类的成员函数声明为友元函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Address;
    class Student{
    private:
    void show(Address *addr);
    };
    class Address{
    public:
    friend void Student::show(Address *addr);
    };
  • 因此Student中show函数就可以访问Address中的成员变量
  • 注意提前声明Address类
  • 需要将show的声明和实现分开,将Address的声明放在中间,这是因为编译器从上到下编译代码,show中函数体用到了Address的成员,如果不提前知道Address的具体声明内容,就不能确定Address是否拥有该成员
  • 一个函数可以被多个类声明为友元函数,这样就可以访问多个类的private成员

友元类

将整个类声明为另一个类的友元类,友元类中所有的成员函数都是另一个类的友元函数

1
2
3
4
5
6
7
8
class Address;
class Student{
...
};
class Address{
public:
friend class Student;
};