1、宏定义
宏定义命令 #define ,
1)不带参数的宏定义
格式:#define 标识符 字符串,其中标识符即为宏名。
作用:在预编译阶段进行宏展开,将宏名替换为定义的字符串。不做语法检查。
说明:a. 宏名一般大写,末尾无分号
b. 宏定义不分配内存,变量定义分配内存
c. 宏定义只对标识符进行替换,不会将包含标识符的字符串进行替换。
例: #define PI 3.1415926
//把程序中出现的PI(标识符)全部换成3.1415926,若程序包含字符串“PI",也不会发生宏替换。
2)带参数的宏定义
格式:#define 宏名(参数list) 字符串。
作用:类似函数调用,但有一个哑实结合(参数传递)的过程。
说明:a. 实参是表达式时需特别注意
#define S(r) r*r
int area = S(a+b); // 宏展开结果 int area = a+b*a+b;
正确定义应为: #define S(r) (r)*(r)
b. 宏名和参数list之间的括号间不能有空格,因为宏定义整体只有3个字段,#define字段,宏名(参数list)字段,字符串字
段。如果有空格,即写成 #define S (r) (r)*(r)形式,会compile error,r not declared。
c. 宏展开(替换)和函数调用的区别
1. 宏替换只做替换,不做计算和表达式求解。
2. 宏替换在编译前运行,不分配内存;函数调用在编译后,程序运行时进行,并分配内存。
3. 宏的哑实结合(传参过程)不存在类型,也没有类型转换。
4. 宏展开使源程序变长,而函数调用不会。
5. 宏展开不占运行时间,只占编译时间;函数调用占用运行时间(分配内存,保存现场,传参,返回值)
应用示例:1. 不用循环和递归,实现打印数字0到999[1]。
#include
#include
using namespace std;
#define A(x) x;x;x;x;x;x;x;x;x;x;
int main(void)
{
int n = 0;
A(A(A(printf("%d\n", n++)))); //A(printf("%d\n", n++))替换为10个输出语句, A(A(A()))1000个
system("pause");
return 0;
}
2、有参宏定义中#和##的用法。
#:将参数两端加上字符串的""符号。
例:#define S(str) #str
S(name) //替换为"name"
##: 字符串连接
例:#define S(str) p##str
S(name) //替换为pname
3、预处理器命令
#define 定义宏
#include包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真 (#ifdef, #ifndef可用来防止头文件重复包含)
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的替代方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
4、栈内存和堆内存
在c++中,内存分为5块,堆、栈、自由存储区、全局/静态存储区和常量存储区。图片来自[2]
堆和栈的区别[3]:
1)、管理方式:堆资源由程序员控制(通过malloc/free、new/delete,容易产生memory leak),栈资源由编译器自动管理。
2)、空间大小:堆是链表存储的不连续区域,受限于计算机的虚拟内存;栈是连续区域,为OS设置好的。
3)、碎片问题:对于堆,频繁的申请/释放内存会造成大量内存碎片,降低效率;对于栈内存,由于FILO结构,不产生碎片。
4)、生长方向:堆向上,向高地址方向增长;栈向下,向低地址方向增长。
5)、分配方式:堆是动态分配;栈有静态分配(如局部遍历)和动态分配alloc函数(变长数组)。
6)、分配效率:堆是C/C++函数库提供的,机制复杂;栈是机器系统提供的数据结构,进出栈都有专门的指令,效率高于堆。
5、static关键字作用
1)修饰变量:
a. 修饰全局变量时,改变全局变量的作用域,使得变量只在本文件可见,对其他文件隐藏。
b. 修饰局部变量时,改变局部变量生存期。只做一次初始化,保持变量内容的持久。在函数内部定义的static局部变量,生存
期为真个源程序,但是作用域仍然限制在函数内部。
c. 修饰类的成员变量。static成员变量时整个类共有,属于类而不属于对象。i.e. 统计该类创建的对象的个数。
class A{
static int count = 0;
}
2)修饰函数
a. 修饰普通函数时,使得函数只在本文件可见,对其他文件隐藏。
b. 修饰类的成员函数时,类的静态成员函数属于整个类而非类的对象,所以它没有this指针,这就导致 了它仅能访问类的静态 数据和静态成员函数
例:在类内部定义static count = 0, 在构造函数count++,可用于统计创建的对象的数量。
修饰成员函数。static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
6、引用和指针的区别[5]
作用:都是间接引用其他对象。
不同:1)指针指向引用对象内存地址,而引用相当于对象的别名,指针可以为空,但引用不可以为空。所以引用在定义时必须初
始化,但指针不必。另外,引用使用时不用判断非空,因此效率比指针高。
2)指针可以被重新赋值以指向另一个不同的对象,但是引用不可以,总是指向初始化时指定的对象。
7、c++类的继承和多态
1)基础知识
多态,即多种形态,当类之间存在层次,且通过继承关联时,会用到多态。c++多态是指在调用函数时,会根据调用对象的类型调用不同的函数
多态分为两种:静态多态/静态链接/早绑定(覆盖)和动态链接(多态)
静态链接/覆盖:基类中定义非虚函数,派生类中定义了同名同参同返回类型的函数。
派生类对象调用函数,为派生类中定义的那个(覆盖了基类的实现)
动态链接(多态):基类中定义virtual 函数,派生类中定义了同名同参同返回类型的函数。实现动态绑定。基类类型对象调用函数为基类实现,继承类对象为继承类实现。具体调用哪个实现由实际运行时对象类型决定。
例[6]:
静态链接
基类
继承类
main()
输出为“Parent class area:",调用的是基类的area()
动态链接-将基类area()定义为virtual
输出为“Rectangle class area:",调用的是继承类的area()。
虚函数: 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
纯虚函数:virtual int area() = 0; 在基类中不给出实现
2)什么函数不能声明为虚函数?[7]
虚函数主要是用来在类的继承时实现多态性(在基类声明为virtual函数,在派生类中再次定义函数,告诉编译器不要静态链接到该函数)。
所以,不能被继承和重写的函数不能声明为虚函数。具体地,普通函数,友元函数(不能被继承),静态成员函数,内联成员函数,构造函数均不可声明为虚函数。
8. const关键字[8, 9]
可修饰内置类型变量,自定义对象,成员函数,函数返回值,函数参数。
1)修饰普通变量-值不可修改
const int a = 8;
a = 9; // error
int *b = (int *)&a;
*b = 9; //error.
2)修饰指针变量
const int * p1 = 8; //指针内容8不可改变
*p1 = 5; //error
int a = 8;
int * const p2 = &a; //指针指向的地址不可变
*p2 = 5; //right
int b = 5;
p2 = &b; // error
const int* const p3 = &a; //内容和指向均不可改变。
*p3 = 0; //error
p3 = &b; //error
口诀: “左定值,右定向,const修饰不变量”
3)参数传递和函数
参数传递分为3种情况:a. 值传递。一般不用const,因为函数会自动产生临时变量复制参数的值
b. 指针传递,可防止指针被意外篡改。
i.e., void fun( int * const p){}
c. 引用传递。对于自定义类型的参数传递,需要临时对象复制参数,需要调用构造函数,比较浪费时间,所
以一般用const type &的方式,防止传入的实参被修改。但对于内置类型int, double等,一般不用引用传递。
修饰函数分2种情况:a. 修饰返回值。
用const来修饰返回的指针或引用,保护指针指向的内容或引用的内容不被修改,也常用于运算符重载。归根
究底就是使得函数调用表达式不能作为左值。如:const A& action (A a);
b. 成员函数后加const,声明函数为只读函数,不能改变类内的数据成员。注意:const函数不能调用非const
i.e., class Account{
int count;
int get_count() const
{ return count; }
}
参考资料:
[1]https://www.cnblogs.com/si-lei/p/9394399.html
[2]https://www.cnblogs.com/ChenZhongzhou/p/5685537.html
[3]https://www.cnblogs.com/yiluyisha/p/9049051.html
[4]https://blog.csdn.net/ll148305879/article/details/92794360
[5]https://www.zhihu.com/question/37608201
[6]https://www.runoob.com/cplusplus/cpp-polymorphism.html
[7]https://blog.csdn.net/judgejames/article/details/87914127
[8]https://www.cnblogs.com/Forever-Kenlen-Ja/p/3776991.html
[9]https://www.cnblogs.com/fan-0802-WHU/p/10961279.html