补充知识点
注意
这里是个人过去遗漏的内容,不系统且不完整,请谨慎参考;
1、宏定义和typedef?
- 宏主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名。
- 宏替换发生在编译阶段之前,属于文本插入替换;typedef是编译的一部分。
宏对比函数调用,同理,宏仅仅是文本替换,不存在调用,返回值,安全检测等操作。
- 宏不检查类型;typedef会检查数据类型。
- 宏不是语句,不在在最后加分号;typedef是语句,要加分号标识结束。
- 注意对指针的操作,typedef char * p_char和#define p_char char *区别巨大。
2、C++中struct和class的区别
相同点
- 两者都拥有成员函数、公有和私有部分
- 任何可以使用class完成的工作,同样可以使用struct完成
不同点
- 两者中如果不对成员不指定公私有,struct默认是公有的,class则默认是私有的
- class默认是private继承, 而struct默认是public继承
扩展:C++和C的struct区别
- C语言中:struct是用户自定义数据类型 (UDT);C++中struct是抽象数据类型(ADT),支持成员函数的定义,(C++中的struct能继承,能实现多态)
- C中struct是没有权限的设置的,且struct中只能是一些变量的集合体,可以封装数据却不可以隐藏数据,而且成员不可以是函数
- C++中,struct增加了访问权限,且可以和类一样有成员函数,成员默认访问说明符为public(为了与C兼容)
- struct作为类的一种特例是用来自定义数据结构的。一个结构标记声明后,在C中必须在结构标记前加上struct,才能做结构类型名(除:typedef struct class;);C++中结构体标记(结构体名)可以直接作为结构体类型名使用,此外结构体struct在C++中被当作类的一种特例
3、C++的顶层const和底层const
概念区分
- 顶层const:指的是const修饰的变量本身是一个常量,无法修改,指的是指针,就是 * 号的右边
- 底层const:指的是const修饰的变量所指向的对象是一个常量,指的是所指变量,就是 * 号的左边
本质上是const修饰的具体内容。
举个例子
int a = 10;
int* const b1 = &a; //顶层const,b1本身是一个常量
const int* b2 = &a; //底层const,b2本身可变,所指的对象是常量
const int b3 = 20; //顶层const,b3是常量不可变
const int* const b4 = &a; //前一个const为底层,后一个为顶层,b4不可变
const int& b5 = a; //用于声明引用变量,都是底层const
区分作用
- 执行对象拷贝时有限制,常量的底层const不能赋值给非常量的底层const
- 使用命名的强制类型转换函数const_cast时,只能改变运算对象的底层const
const int a;
int const a;
const int *a;
int *const a;
- int const a和const int a均表示定义常量类型a。
- const int *a,其中a为指向int型变量的指针,const在 * 左侧,表示a指向不可变常量。(看成const (*a),对引用加const)
- int *const a,依旧是指针类型,表示a为指向整型数据的常指针。(看成const(a),对指针const)
4、C++中const和static对比
static
- 不考虑类的情况
- 隐藏。所有不加static的全局变量和函数具有全局可见性,可以在其他文件中使用,加了之后只能在该文件所在的编译模块中使用
- 默认初始化为0,包括未初始化的全局静态变量与局部静态变量,都存在全局未初始化区
- 静态变量在函数内定义,始终存在,且只进行一次初始化,具有记忆性,其作用范围与局部变量相同,函数退出后仍然存在,但不能使用
- 考虑类的情况
- static成员变量:只与类关联,不与类的对象关联。定义时要分配空间,不能在类声明中初始化,必须在类定义体外部初始化,初始化时不需要标示为static;可以被非static成员函数任意访问。
- static成员函数:不具有this指针,无法访问类对象的非static成员变量和非static成员函数;不能被声明为const、虚函数和volatile;可以被非static成员函数任意访问
const
- 不考虑类的情况
- const常量在定义时必须初始化,之后无法更改
- const形参可以接收const和非const类型的实参
- 考虑类的情况
- const成员变量:不能在类定义外部初始化,只能通过构造函数初始化列表进行初始化,并且必须有构造函数;不同类对其const数据成员的值可以不同,所以不能在类中声明时初始化
- const成员函数:const对象不可以调用非const成员函数;非const对象都可以调用;不可以改变非mutable(用该关键字声明的变量可以在const成员函数中被修改)数据的值 补充一点const相关:const修饰变量是也与static有一样的隐藏作用。只能在该文件中使用,其他文件不可以引用声明使用。 因此在头文件中声明const变量是没问题的,因 为即使被多个文件包含,链接性都是内部的,不会出现符号冲突。
5、数组名和指针(这里为指向数组首元素的指针)区别?
-
二者均可通过增减偏移量来访问数组中的元素。
-
数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。
-
当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了。
6、拷贝初始化和直接初始化
- 当用于类类型对象时,初始化的拷贝形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数。拷贝初始化首先使用指定构造函数创建一个临时对象,然后用拷贝构造函数将那个临时对象拷贝到正在创建的对象。举例如下
string str1("I am a string");//语句1 直接初始化
string str2(str1);//语句2 直接初始化,str1是已经存在的对象,直接调用拷贝构造函数对str2进行初始化
string str3 = "I am a string";//语句3 拷贝初始化,先为字符串”I am a string“创建临时对象,再把临时对象作为参数,使用拷贝构造函数构造str3
string str4 = str1;//语句4 拷贝初始化,这里相当于隐式调用拷贝构造函数,而不是调用赋值运算符函数
- **为了提高效率,允许编译器跳过创建临时对象这一步,直接调用构造函数构造要创建的对象,这样就完全等价于直接初始化了(语句1和语句3等价),但是需要辨别两种情况。
- 当拷贝构造函数为private时:语句3和语句4在编译时会报错
- 使用explicit修饰构造函数时:如果构造函数存在隐式转换,编译时会报错
7、C++有哪几种的构造函数
- 默认构造函数(Default Constructor) 默认构造函数是没有参数的构造函数。当对象创建时,如果没有提供初始化参数,就会调用默认构造函数。编译器会自动生成一个默认构造函数,如果类中没有定义任何构造函数。
class MyClass {
public:
MyClass() { // 默认构造函数
// 初始化代码
}
};