1-数组作为函数的形参会怎么样?int*(*a[3][4])[5]
// 分析:要定义一个数组的指针 等价于 定义一个 数组中首元素的指针
// 首先要知道数组中的元素是什么类型,然后定义出相应的指针
答:会弱化成指针,int *(*(*pa)[4])[5]
2-写一个给数组排序的函数,需要传哪些参数?
答:数组名,数组大小
3-什么是参数缺省?需要注意什么(三点)?
-
概念:函数在定义时预先给出形参的默认值,调用时如果给出实参,则采用实参值,否则采用预先给出的默认形参值。
-
注意:
- 函数的多次声明并且形参值不一样时应注意缺省形参的作用域
- 二义性,参数类型不匹配;明确实参的类型
- 参数缺省;参数不缺省
4-函数重载的注意事项?5点
4-1-重载函数的形参必须不同:个数不同,类型不同,或者不同类型的顺序不同
4-2-不能以形参名区分
4-3-不能以返回值区分
4-4-尽量不要将两个功能不同的函数定义成重载函数,避免出现误解,混淆
4-5-二义性:
4-5-1-参数类型不匹配;办法:明确实参的类型
4-5-2-参数缺省;解决办法:参数不缺省
5-内联函数的作用?以及注意事项(四点)。
-
作用:以代码膨胀作为代价,空间换取时间。编译时在调用处用函数体进行替换,节省了参数传递,控制转移等开销。
-
注意:
-
内联函数体内不能有循环语句和switch语句
-
代码不能过长,最好是不超过 5 行
-
递归函数不能定义成内联函数
-
调用内联函数前,必须已经出现内联函数的定义,而不能只出现内联函数的声明
-
6-用宏定义实现一个返回两个数中较大数的功能。并说出与用普通函数实现的优点。
#define Max(x, y) ((x)>(y)?(x):(y))) // (x):表示 x 是一个不可分割的整体
不同之处 | #define宏 | 函数 |
---|---|---|
代码长度 | 程序在预处理时进行宏替换,代码长度增长 | 使用函数的地方都调用同一份代码,代码长度不变 |
执行时间 | 宏只在预处理时花费时间,所以执行速度较快 | 运行时函数调用需要分配单元,保存现场,值传递和返回的过程中花费时间,所以相对较慢 |
参数类型 | 宏的使用与参数类型无关 | 参数类型必须保持一致 |
可调式性 | 宏内有bug,在运行时不能断点调试 | 函数体内有bug,运行时可以断点调试 |
二义性 | 因为边界问题容易产生二义性如:#define sqrt(x) x*x, 假设x=10+1,则宏展开之后就变成了10+1*10+1 | 函数不会因为边界问题产生二义性 |
递归 | 不支持递归 | 支持递归 |
访问权限 | 在C++中宏不能访问类对象的私有成员 | 成员函数可以访问私有成员 |
7-递归函数的优缺点?构成递归需要具备的条件?
- 优点:
- 递归算法解题通常显得简洁
- 把问题转化为规模缩小了的同类子问题,然后递归调用函数来表示问题的解
- 缺点:
- 效率低
- 系统通过栈来存储每一层的返回点,局部变量,递归次数过多容易造成栈溢出
- 构成条件:
- 子问题必须与原始问题为同样的事,且更为简单(简而治之的思想)
- 不能无限制的调用本身,必须有个出口,化简为非递归状况处理
8-结构体类型变量定义的三种方式?
-
先定义结构体,再说明结构体变量
struct Enemy { 结构体 } ;
Enemy em;
-
定义结构体时说明结构体变量
struct Enemy { 结构体 } em1, em2;
-
直接说明结构体变量,不指定结构体名(一次性定义完变量,后面就不能再定义变量了)
struct { 结构体 } em1, em2;
9-结构体变量用{}初始化时,需要注意什么?
- 必须按照顺序初始化(不能有初始值) Enemy e1 = {19, “anti”};
10-结构体在内存中的存储原则(三点)?
- 各类数据按照一定的规则在空间上排列,而不是顺序的一个接一个排列(字节对齐)
- 结构体的总大小为结构体最宽基本类型的整数倍
- 所有变量存储在自己所占内存的整数倍上
11-枚举类型的变量赋值能用数字赋值么?要用什么赋值?
-
不能,会报错,要用枚举元素赋值
enum DIR {up, down, left, right};
DIR dir;
// dir = 1; // 这个会直接报错
// 只能用枚举元素赋值
dir = up;
12-联合类型变量的初始化是怎么初始化的?
- 在声明语句中初始化联合变量时,总是初始化联合的第一个数据成员。
13-联合类型的在内存中的存储原则?
- 联合类型是将多个数据项组织为一个整体,它们在内存中占用同一段存储单元,联合类型的变量所占用的字节长度为最长的成员的长度。
14-类和结构体中的成员的访问权限分别默认是什么类型?
- 类默认是私有的
- 结构体默认是公有的
- 这也是类和结构体唯一的区别
15-构造函数与析构函数的特征?
- 构造函数:
- 函数名与类名相同
- 无函数类型,无返回值
- 一个新的对象被建立时,该对象隶属类的构造函数自动被调用,对这个对象完成初始化
- 若类中无显示构造函数,则系统自动给出默认构造函数 <类名>(void){};
- 若有多个构造函数,一般它们有不同的参数表和函数体
- 析构函数:
- 名称 ~类名(){};
- 无函数类型,无返回值,没有参数
- 一个类只有一个析构函数,也可以缺省
- 若类中无显示析构函数,则系统自动给出默认的析构函数 ~<类名>(void){}
- 在对象生存期结束时,包括用delete函数释放动态对象时,系统自动调用
16-构造函数与析构函数的调用次数和顺序有什么规律?
-
构造函数与析构函数的调用次数相同
-
先构造内部对象再构造外部对象
-
先析构外部对象再析构内部对象
class A { private: int age; public: A(int age_) { cout << 407 << " A 类带参构造函数被调用 " << endl; this->age = age_; } A() { cout << 408 << "A 类的默认构造函数被调用" << endl; } ~A() { cout << 409 << " A 类的析构函数被调用" << endl; } }; class B { private: A a; // 这里并不会创建对象,类的组合 int weight; public: B(int age_, int weight_) :a(age) { cout << 417 << " B 类的构造函数被调用" << endl; this->weight = weight_; } ~B() { cout << 422 << " B 类的析构函数被调用" << endl; } }; int main() { B(22, 176); // 先构造最里面的对象,然后再构造外层对象 // 析构的时候先析构外层对象,然后再析构内层对象 /* 407 A 类带参构造函数被调用 417 B 类的构造函数被调用 422 B 类的析构函数被调用 409 A 类的析构函数被调用 */ }
17-什么是拷贝构造函数?什么时候调用?
-
本质是构造函数,只是参数为:const 类名 &引用名 // 本类对象的常引用
-
调用:
-
用类的一个对象去初始化另一个对象时
CHero hero_0(200), hero_4(9); CHero hero_1(hero_0); // 显示调用拷贝构造函数 CHero hero_5 = hero_4; // 隐式调用拷贝构造函数 hero_4 = hero_0; // 此处不是调用拷贝构造函数,而是调用赋值运算符函数
-
函数的形参是类的对象,调用函数时或者进行实参和形参结合时
-
如果函数的返回值是类的对象,函数调用完成返回时
-
18-什么是浅拷贝?如何解决浅拷贝问题?
- 浅拷贝不会开辟新的内存空间
- 类中有成员变量为指针,在调用默认的拷贝构造函数,和默认的 = 赋值操作时出现的是浅拷贝
- 重写拷贝构造函数
- 重载 = 操作符
19-初始化类的对象有哪四种方式?要注意什么?
- 默认初始化
- 使用默认构造函数
- 直接初始化
- Gir gir(“name”, “age”);
- 拷贝初始化
- 通过拷贝构造函数来初始化
- 值初始化
- ???