目录
1.构造函数初始化列表
1.1 构造函数初始化列表与函数体内初始化区别
1.2 必须在初始化列表初始化的成员
2. 引用初始化以后不能被改变,指针可以改变所指的对象
3 构造函数与析构函数系列题
3.1构造函数与析构函数的调用次数
4 类的运算符重载
5 类的静态数据成员
5.1 malloc/new/new[]
5.2 new的实现步骤与细节
6 其他于类相关的题目
6.1 空类的大小
6.2 对const变量的修改
volatile
6.3 数值运算符重载
1.构造函数初始化列表
有一个类A,其数据成员如下: 则构造函数中,成员变量一定要通过初始化列表来初始化的是:______。
class A { ... private:int a; public:const int b;float* &c;static const char* d;static double* e; };
A. a b c
B. b c
C. b c d e
D. b c d
E. b
F. c
答案:B
知识点:
1.1 构造函数初始化列表与函数体内初始化区别
一个类,其包含一个类类型成员,对于它的构造函数,如果在函数体内初始化,会先调用其类类型成员的默认构造函数,在调用赋值运算符,而在构造函数初始化时会直接调用它的拷贝构造函数进行初始化
函数体类初始化:
#include <iostream>class B {
public:B() { std::cout << "B defualt construct" << '\n'; }B(int t) : _t(t) { std::cout << "B construct" << '\n'; }B(const B& b) : _t(b._t) { std::cout << "B copy construct" << '\n'; }B& operator=(const B& b) {_t = b._t;std::cout << "B assign operator"<< '\n';return *this;}
private:int _t = 0;
};
class A {
public:A() { std::cout << "A defualt construct" << '\n'; }A(const B& b){ puts("---------------------");_b = b;std::cout << "A construct" << '\n'; }A(const A& a) : _b(a._b) { std::cout << "A copy construct" << '\n'; }A& operator=(const A& a) {_b = a._b;std::cout << "A assign operator" << '\n';return *this;}
private:B _b;
};
int main() {B b(1);A a(b);
}
初始化列表初始化:
#include <iostream>class B {
public:B() { std::cout << "B defualt construct" << '\n'; }B(int t) : _t(t) { std::cout << "B construct" << '\n'; }B(const B& b) : _t(b._t) { std::cout << "B copy construct" << '\n'; }B& operator=(const B& b) {_t = b._t;std::cout << "B assign operator"<< '\n';return *this;}
private:int _t = 0;
};
class A {
public:A() { std::cout << "A defualt construct" << '\n'; }A(const B& b) : _b(b) { puts("---------------------");std::cout << "A construct" << '\n';}/*A(const B& b){ puts("---------------------");_b = b;std::cout << "A construct" << '\n'; }*/A(const A& a) : _b(a._b) { std::cout << "A copy construct" << '\n'; }A& operator=(const A& a) {_b = a._b;std::cout << "A assign operator" << '\n';return *this;}
private:B _b;
};
int main() {B b(1);A a(b);
}
1.2 必须在初始化列表初始化的成员
• const修饰的成员变量
• 引用类型成员
• 类类型成员,且该类没有默认构造函数(由1.1内容可得)
2. 引用初始化以后不能被改变,指针可以改变所指的对象
int main() {int a = 10;int& ref = a; int b = 20; ref = b;std::cout << "a:" << a << " ref:" << ref << " b:" << b; //output:a:20 ref:20 b:20
}
3 构造函数与析构函数系列题
3.1构造函数与析构函数的调用次数
1)
C++语言中,类ClassA的构造函数和析构函数的执行次数分别为()
ClassA *pclassa=new ClassA[5]; delete pclassa;
A. 5,1
B. 1,1
C. 5,5(错误)
D. 1,5
答案:A
2)
#include <iostream>
#include <string>
using namespace std;
class Test {
public:Test(){ std::cout << this << "B defualt construct" << '\n'; }~Test() { std::cout << this << "B destory" << '\n'; }
};
int main() {Test t1;puts("------------");Test* t2;puts("------------");Test t3[3];puts("------------");Test* t4[3]; //t4是存放三个类型Test*的对象的数组puts("------------");Test(*t5)[3]; //t5是数组指针,指向一个存放三个类型为Test的对象的数组puts("------------");
}
打印结果:
4 类的运算符重载
在重载一个运算符为成员函数时,其参数表中没有任何参数,这说明该运算符是 ( )。
A. 无操作数的运算符
B. 二元运算符
C. 前缀一元运算符
D. 后缀一元运算符(错误)
答案:C
例如:
前置++:T& operator++() {}
后置++:T operator++(int) {}
5 类的静态数据成员
下面有关c++静态数据成员,说法正确的是()
A. 不能在类内初始化(错误)
B. 不能被类的对象调用
C. 不能受private修饰符的作用
D. 可以直接用类名调用
答案:D :
知识点:const修饰的静态成员可以在类内初始化,所以A错误
5.1 malloc/new/new[]
malloc/calloc/realloc <----> free new <----> delete new [] <----> delete[]三者一定要匹配使用,否则会产生内存泄漏或者程序崩溃
5.2 new的实现步骤与细节
1) 对于 T*p = new T;
-第一步: 调用operator new(size_t size)申请空间(内部调用malloc循环申请)
-第二步: 调用构造函数完成对申请空间的初始化
对于 delete p;
-第一步:调用析构函数释放p指向的对象中的资源
-第二步:调用operator delete释放p所指向的空间(内部调用free)
2)对于 T*p = new T[N];
-第一步: 调用operator new[](size_t size)申请空间(内部调用operator new(size_t size))
-第二步: 调用N次T的构造函数完成对申请空间的初始化
对于 delete p;
-第一步:调用N次T的析构函数释放p指向的N个对象中的资源
-第二步:调用operator delete[]释放p所指向的空间(内部调用operator delete)
6 其他于类相关的题目
6.1 空类的大小
在Windows 32位操作系统中,假设字节对齐为4,对于一个空的类A,sizeof(A)的值为()? A. 0
B. 1
C. 2
D. 4(错误)
答案:B
类大小的计算方式:与结构体大小的计算方式类似,将类中非静态成员的大小按内存对齐规则计算,并且不用计算成员函数;
特别的,空类的大小在主流的编译器中设置成了1
6.2 对const变量的修改
以下程序输出是____。
#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; }
A. 编译阶段报错运行阶段报错
B. a = 10, *p = 10
C. a = 20, *p = 20(错误)
D. a = 10, *p = 20
E. a = 20, *p = 10
答案:D
知识点:
1)编译器在编译阶段会对const修饰的变量进行优化,将其替换成变量的值
由图中的汇编代码可以看到,打印变量a时,他被直接替换成了10这个常量
volatile
C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
#include <iostream>
using namespace std;
int main(void)
{const int volatile a = 10;int* p = (int*)(&a);*p = 20;cout << "a = " << a << ", *p = " << *p << endl;return 0;
}
当用volatile修饰a之后打印结果为:
6.3 数值运算符重载
下列关于赋值运算符“=”重载的叙述中,正确的是
A. 赋值运算符只能作为类的成员函数重载
B. 默认的赋值运算符实现了“深层复制”功能
C. 重载的赋值运算符函数有两个本类对象作为形参(错误)
D. 如果己经定义了复制拷贝构造函数,就不能重载赋值运算符
答案:A