1. 为什么需要使用泛型?怎么样的代码不使用泛型会显得比较臃肿?
泛型,是一种将类型参数化以达到代码复用的技术,C++ 中使用模板来实现泛型
- 下面的代码不使用泛型会显得比较臃肿?
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
Point add(Point p1, Point p2) {
return p1 + p2;
}
int main() {
add(10, 20);
add(1.2, 1.3);
Point p1(10, 20);
Point p2(100, 200);
add(p1, p2);
return 0;
}
2. C++中如何定义泛型?如何使用泛型技术改造上面的代码?
template <class T>
template <typename T>
template <typename T>
T add(T a,T b) {
return a + b;
}
int main() {
add(10, 20);
add(1.2, 1.3);
Point p1(10, 20);
Point p2(100, 200);
add(p1, p2);
return 0;
}
3. 泛型真的让三个函数变成了一个函数吗? 对不使用泛型技术的 add 函数汇编分析?对使用泛型技术的 add 函数汇编分析?
泛型并不是将三个函数变成一个函数
- 下面的是
不使用泛型技术的 add 函数汇编分析:
- 结论:
泛型是编译器特性,会在编译时期生成对应的类型的函数。
4. 请问add.h、add.cpp、main.cpp
,这三个文件,哪些文件会参与编译
?简述 .cpp
文件变成可执行文件
的过程?
- 只有
.cpp
文件会参与编译
; .h
文件在预处理阶段
,就原封不动的拷贝到 .cpp
文件中了
.cpp
文件变成可执行文件
的过程主要分如下 4 个阶段
- 预处理:将处理一些
预编译指令
,比如 #define #include
- 编译:对代码进行语法分析,词法分析,语义分析等等,最终生成汇编代码
- 汇编:汇编代码转成机器码(前面的三个阶段都是单独对每个 .cpp 文件进行的,一个 .cpp 生成一个 .obj)
- 链接:将多个
目标文件 .obj
,链接生成一个可执行文件(.obj 中很多 call 的地址都是不正确的,因为是单独编译
,链接会处理一些函数地址修正等等问题)
5. 为什么模板的声明和实现如果分离到 .h 和 .cpp 文件中,会导致链接错误
?如何解决?
- ①因为 main.cpp 和 add.cpp 是分开来编译的,编译器在编译 add.cpp 的模板函数式,并不知道要生成何种类型的 add 函数,所以它一个函数都不会生成
- ②在
链接阶段
,main.cpp 中 add 方法调用,需要修复调用地址,这时候才发现并没有对应的方法实现
,所以错误出现在链接阶段
- ③常见链接错误如下:
- 如何解决:一般创建一个
.hpp
文件,将模板的声明和实现都放在 .hpp
文件中。
6. 使用 C++ 实现一个 int型数组类
,有size()、add()、get(int index)、remove(int index)等方法
class Array {
int m_size;
int m_capacity;
int* m_data;
void rangeCheck(int index) {
if (index >= m_size || index < 0) {
throw "数组下标越界了";
}
}
public:
Array(int capacity = 10) {
m_capacity = capacity > 10 ? capacity : 10;
m_data = new int[m_capacity]();
}
int size() {
return m_size;
}
void add(int value) {
if (m_size >= m_capacity) {
throw "数组容量不足";
}
m_data[m_size] = value;
m_size++;
}
int get(int index) {
rangeCheck(index);
return m_data[index];
}
int remove(int index) {
rangeCheck(index);
int oldElement = m_data[index];
for (int i = index; i < m_size - 1; i++) {
m_data[i] = m_data[i + 1];
}
m_size--;
return oldElement;
}
~Array() {
delete m_data;
}
};
7. 如果数组不仅仅需要存储 int,还需要存 double、Point 等等其他类型,要怎么办?
template <class Element>
class Array {
int m_size;
int m_capacity;
Element* m_data;
void rangeCheck(int index) {
if (index >= m_size || index < 0) {
throw "数组下标越界了";
}
}
public:
Array(int capacity = 10) {
m_capacity = capacity > 10 ? capacity : 10;
m_data = new Element[m_capacity]();
}
int size() {
return m_size;
}
void add(Element value) {
if (m_size >= m_capacity) {
throw "数组容量不足";
}
m_data[m_size] = value;
m_size++;
}
Element get(int index) {
rangeCheck(index);
return m_data[index];
}
Element remove(int index) {
rangeCheck(index);
Element oldElement = m_data[index];
for (int i = index; i < m_size - 1; i++) {
m_data[i] = m_data[i + 1];
}
m_size--;
return oldElement;
}
~Array() {
delete m_data;
}
};
8. 如果需要把 Array 作为第三方库给别人使用,需要进行声明和实现分离,要怎么做?
#ifndef Array_hpp
#define Array_hpp
#include <stdio.h>
template <class Element>
class Array {
int m_size;
int m_capacity;
Element* m_data;
void rangeCheck(int index);
public:
Array(int capacity = 10);
int size();
void add(Element value);
Element get(int index);
Element remove(int index);
~Array();
};
template <class Element>
void Array<Element>::rangeCheck(int index) {
if (index >= m_size || index < 0) {
throw "数组下标越界了";
}
}
template <class Element>
Array<Element>::Array(int capacity) {
m_capacity = capacity > 10 ? capacity : 10;
m_data = new Element[m_capacity]();
}
template <class Element>
int Array<Element>::size() {
return m_size;
}
template <class Element>
void Array<Element>::add(Element value) {
if (m_size >= m_capacity) {
throw "数组容量不足";
}
m_data[m_size] = value;
m_size++;
}
template <class Element>
Element Array<Element>::get(int index) {
rangeCheck(index);
return m_data[index];
}
template <class Element>
Element Array<Element>::remove(int index) {
rangeCheck(index);
Element oldElement = m_data[index];
for (int i = index; i < m_size - 1; i++) {
m_data[i] = m_data[i + 1];
}
m_size--;
return oldElement;
}
template <class Element>
Array<Element>::~Array() {
delete m_data;
}
#endif
9. 如果要让 Array 能够使用 array[2],这种下标语法,要怎么办?
template <class Element>
Element Array<Element>::operator[](int index) {
return get(index);
}
10. 如果要让 Array 能够支持 cout << array <<endl 直接打印数组内容,要怎么办?
friend ostream &operator<<(ostream &cout, const Array<Element> &array) {
cout << "[";
for (int i = 0; i < array.m_size; i++) {
if (i != 0) {
cout << ", ";
}
cout << array.m_data[i];
}
return cout << "]";
}