C++入门自学Day5-- c++类与对象(面试题)
c++类与对象往期回顾:
c++类与对象(友元)
c++类与对象(类的初始化和静态成员)
c++类与对象(赋值运算符与拷贝构造)
c++类与对象(拷贝构造)
c++类与对象(构造和析构函数)
c++类与对象(初识2)
c++类与对象(初识)
【 面试题】
问题一:
1、设已经有A,B, C, D4个类的定义,程序中A, B, C, D析构函数调用顺序为(B);
C c;int main(){A a; B b;static D d;return 0;}
选项:
A. D B A C B. B A D C C. C D B A D. A B D C
解题思路:
我们定义一个栈类用来查看栈释放的地址的先后顺序来--> 判断A, B, C, D析构函数调用顺序
class Stack{public:Stack(){_arr = (int*)malloc(sizeof(int)*_capacity);cout<<"malloc "<<_arr<<endl;};~Stack(){cout<<"free "<<_arr<<endl;_arr = nullptr;_size = 0;_capacity = 0;};private:int* _arr;int _size;int _capacity; }; Stack C; int main(){Stack A;Stack B;static Stack D; }
输出描述:
对象的构造顺序
malloc 0x158e05e70 --> C
malloc 0x158e05e10 --> B
malloc 0x118e00000 --> A
malloc 0x158e05fc0 --> D
对象的析构顺序
free 0x118e00000 --> B
free 0x158e05e10 --> A
free 0x158e05fc0 --> D
free 0x158e05e70 --> C
总结:
类别
构造时机
析构时机
存储位置
全局变量
程序开始前(main 前)
程序结束时最后
.data/.bss
局部变量
执行到定义处时
离开作用域(如 main 结束)
栈(stack)
static局部变量
第一次执行到定义时构造
程序结束时(在全局之前)
.data
问题二:
2、以下代码共调用多少次拷贝构造函数:(D)
Widget f(Widget u){Widget v(u);Widget w = v;return w; } int main(){Widget x;Widget y = f(f(x)); }
选项分析:
A. 1 B. 3 C. 5 D. 7
解题思路:
Widget x;
1、首先,把 f(f(x)) 当作整体例如 Widget F,则 Widget y = Widget F。而这里,由于 y 这个对象先前并不存在,所以这里的 “=”并不是赋值运算符,而是发生拷贝构造 --> 1次。
2、 单独看f(x)。f(x)这里把x传入拷贝构造函数,调用拷贝构造 -->2次,然后函数里面,Widget v(u) 调用一次拷贝构造--> 3次.然后 Widget w = v;w先前不存在,这里还是调用拷贝构造 --> 4次,最后这里是传值返回而不是传引用和指针,会进行临时变量的拷贝,又要调用拷贝构造-->5次
3、 看f(f(x)) -->把括号里的f(x)当作一个整体,结果就相当于在调用一次f(x) -->再调用4次拷贝构造 总共9次。
4、但是编译器会对代码进行优化,第4次和第5次会被合二为一,优化成为一个对象,编译器在一个连续的多次的拷贝构造都会被优化成一个拷贝。优化都发生在返回和传参中。所以最终结果是7次。这是在vs2013版本下
vscode代码可视化:
class Widget{ public:Widget(){};Widget (const Widget& w){cout<<"call the copy_constructor!"<<endl;}};Widget f(Widget u){Widget v(u);Widget w = v;return w; } int main(){Widget x;Widget y = f(f(x)); }
输出结果:调用五次拷贝构造。
call the copy_constructor!
call the copy_constructor!
call the copy_constructor!
call the copy_constructor!
call the copy_constructor!对于vscode编译器:
编译器启用了 RVO(Return Value Optimization) 或 Copy Elision(复制省略):
✅ 编译器优化行为:
最终返回的 w(即 return w;)→ 会直接构造到调用者 y 的位置,不再调用拷贝构造。
也有可能优化掉中间的临时对象赋值。
步骤
说明
是否触发拷贝构造
1
x 传入 f(x)
✅ 第一次
2
Widget v(u)
✅ 第二次
3
Widget w = v
✅ 第三次
4
return w(传值返回)
✅ 第四次(可能优化)
5
上一个结果传入 f(...)
✅ 第五次
6
return w 构造给 y
❌(优化成直接构造)