day08
1. 构造函数的实现
请将下列构造函数补充完整,使得程序的运行结果是5
#include<iostream> using namespace std; class Sample { public: Sample(int x) { ________ } ~Sample() { if (p) delete p; } int show() { return *p; } private: int* p; }; int main() { Sample S(5); cout << S.show() << endl; return 0; }
A
*p=x;
Bp=new int(x);
C*p=new int(x);
Dp=&x;
【答案解析】B
通过题目分析得要先调用构造函数,关键就是构造函数该如何实现
首先排除AC选项,因为S.show()函数返回的是对p指针的解引用,AC选项都没有让p指针指向对应空间也就无法进行解引用
而D选项p=&x;
虽然x中存放的值确实是5,但是x是形参,出了构造函数就被销毁了,p也就是野指针,在进行析构delete的时候就会报错
而根据析构中的delete可以推导出构造函数是通过new实现的
2. 编译器对拷贝构造次数的优化
以下代码共调用多少次拷贝构造函数:
Widget f(Widget u) { Widget v(u); Widget w = v; return w; } main() { Widget x; Widget y = f(f(x)); }
A 1
B 3
C 5
D 7
【答案解析】D
day09
3. 构造函数与析构函数
ClassA *pclassa=new ClassA[5];
delete pclassa;
C++语言中,类ClassA的构造函数和析构函数的执行次数分别为()
A 5,1
B 1,1
C 5,5
D 1,5
【答案解析】A
4. C++静态成员特点
下面有关c++静态数据成员,说法正确的是()
A 不能在类内初始化
B 不能被类的对象调用
C 不能受private修饰符的作用
D 可以直接用类名调用
【答案解析】D
先来回顾一下静态成员变量的特点:
- 被所有类对象所共享,并不属于某个具体的对象
- 需要在类中声明,并在类外进行单独定义,不能再构造函数初始化列表的位置进行初始化
- 要在类外进行访问,访问方式:①对象.静态成员变量 ②类名::静态成员变量
针对A选项,对于const int类型的静态成员变量可以直接在类内初始化
针对B选项,可以被类对象调用 :对象.静态成员变量
针对C选项,既然是成员变量,就一定会受修饰符的限制
5. 私有构造/析构
在C++中,为了让某个类只能通过new来创建(即如果直接创建对象,编译器将报错),应该()
A 将构造函数设为私有
B 将析构函数设为私有
C 将构造函数和析构函数均设为私有
D 没有办法能做到
【答案解析】B
如果将构造函数设为私有,那么在new创建对象的过程中,无法调用构造函数报错
如果将析构函数设为私有,那么通过new创建对象是可以的,但是在调用析构函数会报错,解决方案:创建公有的方法来释放开辟的空间
void Release(A*& p)
{
delete p;
}
day10
6. 初始化列表的先后顺序
下面的程序输出可能是什么?
class Printer { public: Printer(std::string name) { std::cout << name; } }; class Container { public: Container() : b("b"), a("a") { } Printer a; Printer b; }; int main() { Container c; return 0; }
A 可能是 "ab" 或 "ba"。 依赖于具体的实现
B 一直都是 "ba"
C 一直都是 "ab"
【答案解析】C
Printer类的构造函数就是打印name Container类的构造函数在初始化列表初始化Printer a、b对象
初始化列表中初始化成员的次序与该成员在初始化列表中出现的先后顺序没有关系
真正的初始化次序与成员变量在类当中生成的先后顺序一致
7. 细节后置
下面 C++ 程序的运行结果是()
#include<iostream> #include<string> using namespace std; class A { friend long fun(A s) { if (s.x < 3) { return 1; } return s.x + fun(A(s.x - 1)); } public: A(long a) { x = a--; } private: long x; }; int main() { int sum = 0; for (int i = 0; i < 5; i++) { sum += fun(A(i)); } cout << sum; return 0; }
A 21
B 15
C 9
D 36
【答案解析】B
8. const对象的使用
以下程序输出是__。
A 编译阶段报错运行阶段报错
B a = 10, p = 10
C a = 20, p = 20
D a = 10, p = 20
E a = 20, p = 10#include <iostream> using namespace std; int main(void) { const int a = 10; int* p = (int*)(&a); *p = 20; cout << "a = " << a << ", *p = " << *p << endl; return 0; }
【答案解析】D
从代码上分析,定义了一个const int类型的变量,不能修改
所以对于&a的类型是const int 所以需要强转 p = 20 将a中的值改为20
那么按照这种分析,打印出来的值就是20 20
==但是C++中const修饰变量会使得该变量变为常量,还具有替换作用
也就是说编译器在编译代码时,在程序中看到对常量当中的内容读取时,会直接使用常量中的内容替换该常量==
所以打印出来的值就是10 20 选D
day11
9. 赋值重载
下列关于赋值运算符“=”重载的叙述中,正确的是
A 赋值运算符只能作为类的成员函数重载
B 默认的赋值运算符实现了“深层复制”功能
C 重载的赋值运算符函数有两个本类对象作为形参
D 如果己经定义了复制拷贝构造函数,就不能重载赋值运算符
【答案解析】A
针对A选项:
针对B选项:
如果用户在类中未显示定义赋值运算符重载,编译器会自动生成一份 ---- 按照浅拷贝的方式
针对C选项:
先来审题:重载的赋值运算符函数有两个本类对象作为形参 而this指针是指针 A* const this
如果说是两个形参的话,该选项是正确的,但是这里是两个对象,所以错误
10. 静态成员变量
下列有关静态成员函数的描述中,正确的是:
A 静态数据成员(非const类型)可以在类体内初始化
B 静态数据成员不可以被类对象调用
C 静态数据成员不受private控制符作用
D 静态数据成员可以直接用类名调用
【答案解析】D
针对A选项,只有const int的静态成员才可以在类内进行初始化 而非const的静态成员是必须在类外初始化的
针对B选项,静态成员的访问方式有两种,分别是用类对象或者类名+作用域进行访问
针对C选项,静态成员也属于成员中的一种,只要是成员就要受访问限定符的约束
11. 构造函数和指针
若PAT是一个类,则程序运行时,语句“PAT(*ad)[3];”调用PAT的构造函数的次数是()
A 2
B 3
C 0
D 1
【答案解析】C
ad是一个数组指针,是指针,该指针将来只能指向PAT类型的3个连续空间
但是这里并没有构造对象,所以没有调用构造函数
12. new的使用
关于以下代码,哪个说法是正确的()
myClass::foo() { delete this; } // .... void func() { myClass* a = new myClass(); a->foo(); }
A 它会引起栈溢出
B 都不正确
C 它不能编译
D 它会引起段错误
【答案解析】B
该代码不存在问题,new出myclass对象给myclass*的指针,然后再delete this 也就是delete a对象
这种写法不推荐,推荐用户直接delete a即可 而不是去调用函数
13. 使用模板进行实例化
有如下模板定义:
template <class T> T fun(T x, T y) { return x * x + y * y; }
在下列对fun的调用中,错误的是()
A fun(1, 2)
B fun(1.0, 2)
C fun(2.0, 1.0)
Dfun<float>(1, 2.0)
【答案解析】B
函数模板的实例化分为两种:
- 隐式实例化 -- 传给函数的两个形参必须一致 例如:fun(1,2) fun(2.0,3.0)
- 显示实例化 -- 两个参数的类型可以不同,但是必须能够发生隐式类型转换,转换成一致的 例如:
fun<int>(1,2.3)
day12
14. 析构函数
下列哪一个是析构函数的特征()
A 析构函数定义只能在类体内
B 一个类中只能定义一个析构函数
C 析构函数名与类名不同
D 析构函数可以有一个或多个参数
【答案解析】B
针对A选项
针对D选项:析构函数只有一个参数
15. 析构函数的限定
如果有一个类是 myClass , 关于下面代码正确描述的是:
myClass::~myClass(){ delete this; this = NULL; }
A 正确,我们避免了内存泄漏
B 它会导致栈溢出
C 无法编译通过
D 这是不正确的,它没有释放任何成员变量
【答案解析】C
所以最终的错误是编译错误
16. 默认成员函数
请问以下说法,哪个是正确的:
A 每个类都有一个无参数的构造函数。
B 每个类都有一个拷贝构造的函数。
C 每个类能有多个构造函数。
D 每个类能有多个析构函数。
【答案解析】C
针对A选项,在类中,如果用户没有显示定义任何构造函数则编译器会生成一个无参的构造函数,但是用户一旦声明,编译器就不再生成
针对B选项,C++11特性中类中可以不用定义拷贝构造函数
代码:A(A&) = delete;
(这样也就不存在拷贝构造函数了)
针对D选项,析构函数只有一个
17. 动静态分配
C++中关于堆和栈的说法,哪个是错误的()
A 堆的大小仅受操作系统的限制,栈的大小一般较小
B 在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题
C 堆和栈都可以静态分配
D 堆和栈都可以动态分配
【答案解析】C
动态分配:在程序运行时才知道开辟空间的大小
静态分配:在编译期间就已经知道所需空间大小
day13
18. 虚函数
关于虚函数的描述正确的是()
A 派生类的虚函数与基类的虚函数具有不同的参数个数和类型
B 内联函数不能是虚函数
C 派生类必须重新定义基类的虚函数
D 虚函数可以是一个static型的函数
【答案解析】B
==内联函数为啥不能作为虚函数呢?==
因为内联函数是在调用时展开 也就意味着其不存在函数地址,而虚函数是需要将函数地址放到虚表当中的
虚函数是不能定义成static类型的,因为static类型不属于成员函数(不带this指针),
而虚函数的调用必须依赖于具体的对象,必须是父类的指针或引用指向子类的对象(形成多态的前提)或者就是父类对象
19. 常量指针和指针常量
下列描述,正确的一共有多少个()
1)const char p,这是一个常量指针,p的值不可修改
2)在64位机上,char p= “abcdefghijk”; sizeof(p)大小为12
3)inline会检查函数参数,所以调用开销显著大于宏
4)重载是编译时确定的,虚函数是运行时绑定的
A 1 B 2 C 3 D 4
【答案解析】A
针对A选项,首先需要认识常量指针和指针常量的区别(可以看该博客:指针常量和常量指针的区别)
简单来讲: const XXX p 就是常量指针, XXX const p 就是指针常量
A选项虽然是常量指针,但是p的值可以修改,只是p所指向对象的值不可修改
针对C选项,内联函数和宏定义在调用时都会展开,所以调用开销类似
D选项正确
20. 父类析构设置为虚函数的原因
C++将父类的析构函数定义为虚函数,下列正确的是哪个()
A 释放父类指针时能正确释放子类对象
B 释放子类指针时能正确释放父类对象
C 这样做是错误的
D 以上全错
【答案解析】A
如果不将析构函数定义为虚函数,那么delete调用时根据指针类型去释放对象(普通调用)。
当有父类的指针指向子类对象时(多态的条件之一),此时是普通调用,那么只能释放父类部分,而子类部分无法释放(),造成内存泄漏
所以将父类的析构函数定义为虚函数是为了在释放父类指针时,也能正确释放子类对象
21. 编译器优化拷贝构造次数
分析一下这段程序的输出
#include <iostream> using namespace std; class B { public: B() { cout << "default constructor" << " "; } ~B() { cout << "destructed" << " "; } B(int i) : data(i) { cout << "constructed by parameter" << data << " "; } private: int data; }; B Play(B b) { return b; } int main(int argc, char* argv[]) { B temp = Play(5); return 0; }
A constructed by parameter5 destructed destructed
B constructed by parameter5 destructed
C default constructor" constructed by parameter5 destructed
D default constructor" constructed by parameter5 destructed destructed
【答案解析】A
先是通过5参数构造B对象 Play方法当中通过传值传参 1次拷贝构造 传值返回 1次拷贝构造 ( 但是编译器会优化将这两次优化成1次 )
所以拷贝构造出来的临时对象(虽然没写,但有默认的)也会去调用析构函数,所以一共有两次析构
选A