C++ 面向对象编程
一个类可以定义无数个对象,每一个对象都有自己的成员变量,但是他们共享一套成员方法。
构造函数的初始化列表和直接在构造函数中构造的区别:
初始化列表是用来初始化成员类的,用来调用成员的构造函数的
一个是先调用默认构造后初始化,一个是调用构造函数初始化
即:
int a = 10
和int a; a = 10
的区别。对于普通类型区别不大。
初始化列表的默认初始化顺序:成员函数的定义顺序。
静态成员变量:类内声明,类外定义和初始化,不属于对象,属于类级别。
静态成员方法:可以用类名调用。(普通成员函数不能用类名调用的原因:需要隐式传入成员 this 指针,不传缺少参数无法编译。)
常成员方法:接收的 this 指针为 const 类型。
模版函数:建议定义在头文件
辨析:为什么模版函数,内联函数要定义在头文件中,普通函数不建议定义在头文件中?
原因:在函数编译链接过程中,函数/全局变量/静态变量/常量常量变量等会产生符号,且定义在 头文件中的普通函数将会展开,在链接时,连接器如果发现同名的函数符号,会直接报错(error: multiple definition of ‘yourFunction’)。而对于模版函数和内联函数会生成一个弱符号(weak sym),连接器会从多个头文件展开后的源文件中选取一个链接,不会因出现同名弱符号而报错。且由于模版本身是不编译的,所以在一个文件中定义的模版一般无法在另一个文件中使用,因为模版不编译而不产生符号,导致使用的文件产生的 “UND” 找不到对应的函数定义。(有声明未定义的函数会别标志为“UND”并且在链接过程中在其他 .o 文件中寻找定义。)
new 和 delete 重载的应用 -> 对象池
#include <iostream>
using namespace std;
template <class T>
class Queue {
public:
explicit Queue(const T& data = T())
: _front(new QueueItem(data)) { _rear = _front; }
~Queue() {
if (empty()) {
delete _front;
return;
}
// 略
}
void push(const T& data) {
QueueItem* temp = new QueueItem(data);
_rear->_next = temp;
_rear = temp;
}
void pop() {
if (empty()) {perror("Queue is empty"); exit(1);}
auto temp = _front->_next;
delete _front;
_front = temp;
if (_front->_next == nullptr) {
_rear = _front;
}
}
[[nodiscard]] T& front() const {
if (empty()) {perror("Queue is empty"); exit(1);}
return _front->_next->_data;
}
[[nodiscard]] bool empty() const {
return _front->_next == nullptr;
}
private:
struct QueueItem {
QueueItem(T data = T())
: _next(nullptr), _data(data) {}
void* operator new(size_t size) {
if (_itemPool == nullptr) {
_itemPool = (QueueItem *)new char[POOL_SIZE * sizeof(QueueItem)];
auto p = _itemPool;
for (; p < _itemPool + POOL_SIZE - 1; p++) {
p->_next = p + 1;
}
p->_next = nullptr;
}
auto p = _itemPool;
_itemPool = _itemPool->_next;
return p;
}
void operator delete(void* p) {
auto *ptr = (QueueItem *)p;
ptr->_next = _itemPool;
_itemPool = ptr;
}
QueueItem* _next;
T _data;
static QueueItem *_itemPool;
static const int POOL_SIZE = 10000;
};
QueueItem *_front; // 指向头节点
QueueItem *_rear; // 指向尾节点
};
template <class T>
Queue<T>::QueueItem *Queue<T>::QueueItem::_itemPool = nullptr;
int main() {
Queue<int> que;
for (int i = 0; i < 100; i++) {
que.push(1);
que.push(2);
que.push(3);
que.push(4);
que.push(5);
}
for (int i = 0; i < 20; i++) {
cout << que.front() << endl;
que.pop();
}
return 0;
}
可通过重载 new 和 delete 运算符实现顺序链表的连接池。