文章目录

一、实现复数运算符

#include<iostream>

using namespace std;

class Plural {
private:
int _real;
int _vir;

public:
Plural():_real(int()),_vir(int()) {
cout << "Plural()" << endl;
}

Plural(int real, int vir) {
_real = real;
_vir = vir;
cout << "Plural(int real, int vir)" << endl;
}

Plural(const Plural& src):_real(src._real), _vir(src._vir) {
cout << "Plural(const Plural& src)" << endl;
}

// 这里加&,主要是为了节省空间时间,不需要再构造对象
// 而不加&,则需要构造新对象
// 新的对象在return前构造,调用处那一行结束后死亡

Plural& operator=(const Plural& src) {
cout << "Plural& operator=(const Plural& src)" << endl;
_real = src._real;
_vir = src._vir;
return *this;
}

~Plural() {
cout << "~Plural()" << endl;
}

// 这里不可返回Plural&,如果返回引用,当栈帧清除后,局部变量就销毁了
Plural operator+(/* *this */const Plural& src) {

Plural res;
res._real = _real + src._real;
res._vir = _vir + src._vir;
return res; // 这里返回的时候会调用拷贝构造,生成一个对象,将值带给f3

// return Plural(_real + src._real, _vir + src._vir);
}

void show() const {
cout << _real << "+" << _vir << "i" << endl;
}
};

int main() {
// 因为f1是正在生成的对象,所以这里的=调用拷贝构造函数,而不调用等号运算符
Plural f1 = Plural(2, 3) + Plural(2, 3);
Plural f2;
// 这里两个对象都是已存在的对象,所以调用等号运算符
f2 = f1;
return 0;
}

拷贝构造 :已存在对象和正在生成对象之间的操作

等号运算符 :已经存在的对象之间的操作

C++运算符重载_运算符


简单运算符重载

// 这里不可返回Plural&,如果返回引用,当栈帧清除后,局部变量就销毁了
Plural operator+(/* *this */const Plural& src) {

Plural res;
res._real = _real + src._real;
res._vir = _vir + src._vir;
return res; // 这里返回的时候会调用拷贝构造,生成一个对象,将值带给f3

// return Plural(_real + src._real, _vir + src._vir);
}

Plural operator-(/* *this */const Plural& src) {

Plural res;
res._real = _real - src._real;
res._vir = _vir - src._vir;
return res; // 这里返回的时候会调用拷贝构造,生成一个对象,将值带给f3

// return Plural(_real - src._real, _vir - src._vir);
}

bool operator>(/* *this */const Plural& src) {
if (_real > src._real) {
return true;
}
if (_real == src._real && _vir > src._vir) {
return true;
}
return false;
}

bool operator==(/* *this */const Plural& src) {
return _real == src._real && _vir == src._vir;
}

重载​​<<​​运算符

void operator<< (/* *this */ostream& out) {
out << _real << "+" << _vir << "i" << endl;
}

上述写法导致双目运算符​​<<​​​左侧是输出的对象,右侧是流对象
然而一般来讲,对于​​​<<​​​和​​>>​​来说,左侧是流对象,右侧是输出的对象

调用如下:

int main() {
Plural p(1, 2);
p. operator<< (cout);
return 0;
}

类外重载​​<<​​​和​​>>​​即可

ostream& operator<<(ostream& out, const Plural& p) {
cout << p.get_real() << "+" << p.get_vir() << "i" << endl;
}

调用如下:

int main() {
Plural p(1, 2);
cout << p << endl;
return 0;
}

类外实现重载​​>>​​即可

istream& operator>>(istream& in, Plural& p) {
int real;
int vir;
in >> real >> vir;
p.set_real(real);
p.set_vir(vir);
return in;
}
int main() {
Plural p;
cin >> p;
return 0;
}

重载++、- -

// 前置++
// 这里返回引用,实际上就是返回的自己,也就是调用对象
Plural& operator++(/*this*/) {
_real++;
_vir++;
return *this;
}

// 后置++,其中int只作为后置的标志,没有任何作用
Plural operator++(/*this*/ int) {
Plural tmp = *this;
_real++;
_vir++;
return tmp;
}

Plural& operator--(/*this*/) {
_real--;
_vir--;
return *this;
}

// 后置--,其中int只作为后置的标志,没有任何作用
Plural operator--(/*this*/ int) {
Plural tmp = *this;
_real--;
_vir--;
return tmp;
}
int main() {
Plural p1(1, 2);
Plural p2;
Plural p3;
p3 = p2 = ++p1; // p2.operator=(p1.operator++());
p2++;
p2.show();
p1.show();
return 0;
}

二、实现类 ​​Mstring​​​ ,实现​​string​​的部分功能

#ifndef MSTRING_H
#define MSTRING_H
#define DEFAULT_LEN 10
#include<iostream>
using namespace std;

class Mstring {
private:
char* _str;
unsigned int _storage; // 容量
unsigned int _len; // 使用的长度(包括'\0')

bool is_full() const{
return _len == _storage;
}

bool is_empty() const{
return _len == 1;
}

void expand() {
if (0 == _storage) {
_storage = 10;
}
_storage = _storage + (_storage >> 1);
char* tmp = new char[_storage];
strcpy_s(tmp, _len, _str);

// 释放原空间
if (NULL != _str) {
delete[]_str;
_str = NULL;
}
_str = tmp;
}

public:
// 这个1表示'\0'
Mstring():_str(NULL), _storage(DEFAULT_LEN), _len(1){
_str = new char[DEFAULT_LEN];
_str[_len - 1] = '\0';
}

Mstring(const char* str) :_str(NULL), _storage(DEFAULT_LEN), _len(1) {
while (_storage < strlen(str) + 1) {
_storage = _storage + (_storage >> 1);
}
_len = strlen(str) + 1;
// 堆区申请
_str = new char[_storage];
strcpy_s(_str, _len, str);
}

Mstring(const Mstring& src) {
_storage = src._storage;
_len = src._len;
_str = new char[_storage];
strcpy_s(_str, _len, src._str);
}

Mstring& operator=(const Mstring& src) {
// 防止自赋值
if (this == &src) {
return *this;
}
// _str不空的时候,全部释放
if (NULL != _str) {
delete[]_str;
}
_storage = src._storage;
_len = src._len;

_str = new char[_storage];
strcpy_s(_str, _len, src._str);

return *this;
}

~Mstring() {
if (NULL != _str) {
delete[]_str;
_str = NULL;
}
}

const char* get_str() const{
return _str;
}

void push_back(char c) {
if (is_full()) {
expand();
}
// 若_len=5,则字符串长度只有4,应该在_str[_len-1]的位置插入字符
_str[_len - 1] = c;
// 若当前的字符为'\0',插入了这个'\0'后无需再到后面再插入'\0'
if (c != '\0') {
_str[_len++] = '\0';
}

}

char pop_back() {
if (is_empty()) {
return '\0';
}
else {
char tmp = _str[_len - 2];
_str[_len - 2] = '\0';
_len--;
return tmp;
}
}

bool operator > (const Mstring& src)const {
return strcmp(_str, src._str) > 0;
}

bool operator == (const Mstring& src) const {
return strcmp(_str, src._str) == 0;
}

Mstring operator + (const Mstring& src){
while (_storage < _len + src._len) {
expand();
}
char* tmp = new char[_storage];
strcpy_s(tmp, _len, _str);
for (int i = 0; i < src._len; i++) {
tmp[i + _len - 1] = src._str[i];
}
return Mstring(tmp);
}

// 普通对象调用,允许修改,返回引用
char& operator[](int pos) {
return _str[pos];
}

// const对象调用,不允许修改,返回临时对象
char operator[](int pos) const {
return _str[pos];
}

unsigned int size() {
return _len - 1;
}
};


ostream& operator << (ostream& out, const Mstring& src) {
out << src.get_str() << endl;
return out;
}


#endif

Mstring 写时拷贝代码

#ifndef MSTRING_H
#define MSTRING_H
#define DEFAULT_STORAGE 10
#define CNT_LEN 8

#include<iostream>

using namespace std;

class Mstring {
private:
char* _str;
unsigned int _storage; // 容量
unsigned int _len; // 使用的长度(包括'\0')

bool is_full() const{
return _len == _storage;
}

bool is_empty() const{
return _len == 1;
}

void expand() {
if (0 == _storage) {
_storage = 10;
}
_storage = _storage + (_storage >> 1);
char* tmp = new char[_storage];
strcpy_s(tmp, _len, _str);

// 释放原空间
if (NULL != _str) {
delete[]_str;
_str = NULL;
}
_str = tmp;
}

// 返回存储字符空间的首地址
char* get_str_begin() {
return _str + CNT_LEN;
}

int& get_str_cnt() {
// 将_str转成int*,则指针直接指向4字节空间,解引用后返回
return *((int*)_str);
}

int& get_str_cnt() const{
// 将_str转成int*,则指针直接指向4字节空间,解引用后返回
return *((int*)_str);
}

void add_cnt() {
get_str_cnt()++;
}

void destory_str() {
if (NULL != _str && get_str_cnt() - 1 == 0) {
delete[] _str;
_str = NULL;
_len = 0;
_storage = 0;
}
else {
get_str_cnt()--;
}
}

// 把原来对象的数据拷贝到新的对象
void copy_while_write() {
// 当前指向的字符串引用计数大于1时才需要分配新的空间,等于1时直接写
if (NULL != get_str_cnt() > 1) {
char* tmp = new char[_len];
// 拷贝字符数据到tmp
strcpy_s(tmp + CNT_LEN, _len - CNT_LEN, get_str_begin());
// 之前_str的引用计数-1
get_str_cnt()--;
//现在对象的_str指向刚生成的空间
_str = tmp;
// 当前对象的引用计数为1
get_str_cnt() = 1;
}
}

public:
// 这个1表示'\0'
Mstring():_str(NULL), _storage(DEFAULT_STORAGE), _len(CNT_LEN + 1){
_str = new char[DEFAULT_STORAGE];
_str[_len - 1] = '\0';
get_str_cnt() = 1;
}

Mstring(const char* str) :_str(NULL), _storage(DEFAULT_STORAGE), _len(CNT_LEN + 1) {
while (_storage < strlen(str) + 1 + CNT_LEN) {
_storage = _storage + (_storage >> 1);
}
// 堆区申请
_str = new char[_storage];

// 总共使用的长度
_len = strlen(str) + 1 + CNT_LEN;
strcpy_s(get_str_begin(), _len - CNT_LEN, str);

get_str_cnt() = 1;
}

Mstring(const Mstring& src) {
_storage = src._storage;
_len = src._len;
// 写时拷贝,刚生成,这时没有写数据,所以不需要分配空间,指向即可
_str = src._str;
add_cnt();
}

Mstring& operator=(const Mstring& src) {
// 防止自赋值
if (this == &src) {
return *this;
}
// _str不空的时候,全部释放
if (NULL != _str) {
destory_str();
}
_storage = src._storage;
_len = src._len;

_str = src._str;
add_cnt();
return *this;
}

~Mstring() {
destory_str();
}

const char* get_str() const{
return _str + CNT_LEN;
}

void push_back(char c) {
copy_while_write();
if (is_full()) {
expand();
}
// 若_len=5,则字符串长度只有4,应该在_str[_len-1]的位置插入字符
_str[_len - 1] = c;
// 若当前的字符为'\0',插入了这个'\0'后无需再到后面再插入'\0'
if (c != '\0') {
_str[_len++] = '\0';
}

}

char pop_back() {
copy_while_write();
if (is_empty()) {
return '\0';
}
else {
char tmp = _str[_len - 2];
_str[_len - 2] = '\0';
_len--;
return tmp;
}
}

bool operator > (const Mstring& src)const {
return strcmp(_str + CNT_LEN, src._str+ CNT_LEN) > 0;
}

bool operator == (const Mstring& src) const {
return strcmp(_str + CNT_LEN, src._str + CNT_LEN) == 0;
}

Mstring operator + (const Mstring& src){
while (_storage < _len + src._len) {
expand();
}

char* tmp = new char[_storage];
strcpy_s(tmp + CNT_LEN, _len - CNT_LEN, get_str_begin());

for (int i = 0; i < src._len - CNT_LEN; i++) {
tmp[i + _len - 1] = src._str[i + CNT_LEN];
}
return Mstring(tmp);
}

// 普通对象调用,允许修改,返回引用
// 有一点问题,如果是str[1] = 's'调用了写时拷贝
// cout<<str[1]<<endl 也调用了写时拷贝

char& operator[](int pos) {
if (NULL != _str) {
copy_while_write();
}
return _str[pos + CNT_LEN];
}

// const对象调用,不允许修改,返回临时对象
char operator[](int pos) const {
return _str[pos + CNT_LEN];
}

// 减去'\0'和引用计数长度
unsigned int size() {
return _len - 1 - CNT_LEN;
}

};
#endif

/*
在mstring.cpp中实现
ostream& operator << (ostream& out, const Mstring& src) {
out << src.get_str() << endl;
return out;
}
*/

Mstring迭代器实现

​mstring.h​

#ifndef MSTRING_H
#define MSTRING_H
#define DEFAULT_STORAGE 16
#define CNT_LEN 8// 写时拷贝,记录引用计数
#include<iostream>
#include"mstring_iterator.h"

using namespace std;

class Mstring {
private:
char* _str;
unsigned int _storage; // 容量
unsigned int _len; // 使用的长度(包括'\0')

bool is_full() const{
return _len == _storage;
}

bool is_empty() const{
return _len == 1;
}

void expand() {
_storage = _storage + (_storage >> 1);
char* tmp = new char[_storage];
strcpy_s(tmp, _len, _str);

// 释放原空间
if (NULL != _str) {
delete[]_str;
_str = NULL;
}
_str = tmp;
}

// 返回存储字符空间的首地址
char* get_str_begin() {
return _str + CNT_LEN;
}

int& get_str_cnt() {
// 将_str转成int*,则指针直接指向4字节空间,解引用后返回
return *((int*)_str);
}

int& get_str_cnt() const{
// 将_str转成int*,则指针直接指向4字节空间,解引用后返回
return *((int*)_str);
}

void add_cnt() {
get_str_cnt()++;
}

void destory_str() {
if (NULL != _str && get_str_cnt() - 1 == 0) {
delete[] _str;
_str = NULL;
_len = 0;
_storage = 0;
}
else {
get_str_cnt()--;
}
}

// 把原来对象的数据拷贝到当前的对象
void copy_while_write() {
// 当前指向的字符串引用计数大于1时才需要分配新的空间,等于1时直接写
if (NULL != get_str_cnt() > 1) {
char* tmp = new char[_len];
// 拷贝字符数据到tmp
strcpy_s(tmp + CNT_LEN, _len - CNT_LEN, get_str_begin());
// 之前_str的引用计数-1
get_str_cnt()--;
//现在对象的_str指向刚生成的空间
_str = tmp;
// 当前对象的引用计数为1
get_str_cnt() = 1;
}
}

public:
typedef Mstring_Iterator iterator;

Mstring_Iterator begin() {
return Mstring_Iterator(*this, 0);
}

Mstring_Iterator end() {
return Mstring_Iterator(*this, _len - CNT_LEN - 1);
}
// 这个1表示'\0'
Mstring():_str(NULL), _storage(DEFAULT_STORAGE), _len(CNT_LEN + 1){
_str = new char[DEFAULT_STORAGE];
_str[_len - 1] = '\0';
get_str_cnt() = 1;
}

Mstring(const char* str) :_str(NULL), _storage(DEFAULT_STORAGE), _len(CNT_LEN + 1) {
while (_storage < strlen(str) + 1 + CNT_LEN) {
_storage = _storage + (_storage >> 1);
}
// 堆区申请
_str = new char[_storage];

// 总共使用的长度
_len = strlen(str) + 1 + CNT_LEN;
strcpy_s(get_str_begin(), _len - CNT_LEN, str);

get_str_cnt() = 1;
}

Mstring(const Mstring& src) {
_storage = src._storage;
_len = src._len;
// 写时拷贝,刚生成,这时没有写数据,所以不需要分配空间,指向即可
_str = src._str;
add_cnt();
}

Mstring& operator=(const Mstring& src) {
// 防止自赋值
if (this == &src) {
return *this;
}
// _str不空的时候,全部释放
if (NULL != _str) {
destory_str();
}
_storage = src._storage;
_len = src._len;

_str = src._str;
add_cnt();
return *this;
}

~Mstring() {
destory_str();
}

const char* get_str() const{
return _str + CNT_LEN;
}

void push_back(char c) {
copy_while_write();
if (is_full()) {
expand();
}
// 若_len=5,则字符串长度只有4,应该在_str[_len-1]的位置插入字符
_str[_len - 1] = c;
// 若当前的字符为'\0',插入了这个'\0'后无需再到后面再插入'\0'
if (c != '\0') {
_str[_len++] = '\0';
}

}

char pop_back() {
copy_while_write();
if (is_empty()) {
return '\0';
}
else {
char tmp = _str[_len - 2];
_str[_len - 2] = '\0';
_len--;
return tmp;
}
}

bool operator > (const Mstring& src)const {
return strcmp(_str + CNT_LEN, src._str+ CNT_LEN) > 0;
}

bool operator == (const Mstring& src) const {
return strcmp(_str + CNT_LEN, src._str + CNT_LEN) == 0;
}

// 判断内容
bool operator != (const Mstring& src) const {
return strcmp(_str + CNT_LEN, src._str + CNT_LEN) != 0;
}

Mstring operator + (const Mstring& src){
while (_storage < _len + src._len) {
expand();
}

char* tmp = new char[_storage];
strcpy_s(tmp + CNT_LEN, _len - CNT_LEN, get_str_begin());

for (unsigned int i = 0; i < src._len - CNT_LEN; i++) {
tmp[i + _len - 1] = src._str[i + CNT_LEN];
}
return Mstring(tmp);
}

// 普通对象调用,允许修改,返回引用
// 有一点问题,如果是str[1] = 's'调用了写时拷贝
// cout<<str[1]<<endl 也调用了写时拷贝

char& operator[](int pos) {
copy_while_write();
return _str[pos + CNT_LEN];
}

// const对象调用,不允许修改,返回临时对象
char operator[](int pos) const {
return _str[pos + CNT_LEN];
}

// 减去'\0'和引用计数长度
unsigned int size() {
return _len - 1 - CNT_LEN;
}

Mstring_Iterator erase(Mstring_Iterator& iter) {
// 当前字符串为空,只有一个'\0'
if (is_empty()) {
return iter;
}
for (unsigned int i = iter.get_pos(); i < _len - CNT_LEN - 1; i++) {
get_str_begin()[i] = get_str_begin()[i + 1];
}
_len--;
return Mstring_Iterator(*this, iter.get_pos());
}

// 迭代器指向的位置插入字符,返回迭代器位置对应的的迭代器
Mstring_Iterator insert(Mstring_Iterator& iter, char c) {
// 把原来对象的数据拷贝到当前的对象
copy_while_write();
if (is_full()) {
expand();
}
unsigned int i;
for (i = _len - CNT_LEN - 1; i >= iter.get_pos(); i--) {
get_str_begin()[i + 1] = get_str_begin()[i];
}
get_str_begin()[++i] = c;
_len++;
return Mstring_Iterator(*this, iter.get_pos());
}

friend ostream& operator<<(ostream& out, const Mstring& str);
};


#endif

​mstring_iterator.h​

#pragma once
#ifndef MSTRING_ITERATOR_H
#define MSTRING_ITERATOR_H
#include <iostream>

using namespace std;

class Mstring;

class Mstring_Iterator {
private:
Mstring& _mstring;
unsigned int _pos; // 这个_pos表示字符串对应的索引

public:
Mstring_Iterator(Mstring& str, int pos):_mstring(str), _pos(pos) {

}
Mstring_Iterator(const Mstring_Iterator& src):_mstring(src._mstring), _pos(src._pos) {

}

Mstring_Iterator& operator=(const Mstring_Iterator& src) {
// 如果当前迭代器指向的字符串地址相同,则修改_pos
//if (_mstring.get_str() == src._mstring.get_str()) {
//if ((&_mstring)->get_str() == src._mstring.get_str()) {
if (&_mstring == &(src._mstring)) {
_pos = src._pos;
}
return *this;
}

// 当前的_mstring是引用,只能取地址,不是实例化对象,不能调用方法或者属性
bool operator!=(const Mstring_Iterator& src) {
return &_mstring != &(src._mstring) || _pos != src._pos;
}
bool operator==(const Mstring_Iterator& src) {
return &_mstring == &(src._mstring) && _pos == src._pos;
}

Mstring_Iterator operator++() {
_pos++; // 先增加
return *this;//返回增加后的值
}

Mstring_Iterator operator++(int) {
Mstring_Iterator tmp = *this;
_pos++;
return tmp;//返回增加前的值
}
Mstring_Iterator operator--() {
_pos--;
return *this;
}
Mstring_Iterator operator--(int) {
Mstring_Iterator tmp = *this;
_pos--;
return tmp;
}

Mstring_Iterator operator+(int step) {

return Mstring_Iterator(_mstring, _pos + step);
}
Mstring_Iterator operator-(int step) {
return Mstring_Iterator(_mstring, _pos - step);
}

char& operator*();

unsigned int get_pos() {
return _pos;
}
};
#endif

​mstring.cpp​

#include "mstring.h"
#include <iostream>

// 不属于某个类,参数给对象即可
ostream& operator << (ostream& out, const Mstring& src) {
out << src.get_str() << endl;
return out;
}

char& Mstring_Iterator::operator*(){
return Mstring_Iterator::_mstring[_pos];
}

​main.cpp​

#include<iostream>
#include<string.h>
#include"mstring.h"

using namespace std;

int main(){
Mstring str("012345");
cout << str << endl;
for (Mstring::iterator iter = str.begin(); iter != str.end(); iter++) {
cout << (*iter) << endl;
}
Mstring::iterator iter1 = str.begin();
for (; iter1 != str.end(); iter1++) {
if (*iter1 == '3') {
str.insert(iter1, 'p');
iter1++;
}
}
cout << str << endl;
return 0;
}

三、深入理解new和delete

​malloc​​​和​​new​​的区别

  1. ​malloc​​​是按字节开辟空间的,​​new​​开辟内存时需要指定类型(​​new int()​​),​​malloc​​开辟内存返回的都是​​void*​​,而​​new​​返回的是对应类型的指针
  2. ​malloc​​​负责开辟空间,​​new​​不仅有​​malloc​​的功能,还可以进行数据初始化,比如:​​new int(10)​​。​​new​​有开辟空间和构造的功能。
  3. ​malloc​​​开辟内存失败返回​​nullptr​​,而​​new​​则会抛出​​bad_alloc​​异常
  4. 我们调用​​new​​实际上是调用的​​operator new​

注:栈区分配空间可以用_alloca(n)

​free​​​和​​delete​​的区别

  1. ​delete​​​先调用析构函数,再释放空间(即​​free​​)
  2. 我们调用​​delete​​实际上是调用的​​operator delete​
// new实际上先调用operator new开辟内存空间,然后调用对象的构造函数
void* operator new(size_t size) {
void* p = malloc(size);
if (p == nullptr) {
throw bad_alloc();
}
cout << "operator new addr:"<< p << endl;
return p;
}

// delete实际上先调用对象的析构函数,然后调用operator delete回收内存空间,
void operator delete(void* ptr) {
cout << "operator free addr:" << ptr << endl;
free(ptr);
}

// 用于数组
void* operator new[](size_t size) {
void* p = malloc(size);
if (p == nullptr) {
throw bad_alloc();
}
cout << "operator new[] addr:" << p << endl;
return p;
}


void operator delete[](void* ptr) {
cout << "operator free[] addr:" << ptr << endl;
free(ptr);
}

​new​​​和​​delete​​能混用吗?C++为什么区分单个元素和数组的内存分配和释放?

对于基本数据类型而言,​​new​​​和​​delete​​​的功能就是​​malloc​​​和​​free​​​的功能,也可以混用​​delete​​​和​​delete[]​

int* p = new int();
delete[] p;

int* q = new int[10];
delete q;

但是对于对象而言,就不能混用

错误代码如下:

int main() {
try {
Test* p = new Test[5];
cout << "开辟空间后返回的地址:" << p << endl;
delete p; // 调用析构,不会查看前4个字节确定数组元素的个数
// delete[]p; //会查看前4个字节确定数组元素的个数
}
catch(const bad_alloc& err){
cerr << err.what() << endl;
}
return 0;
}

C++运算符重载_c++_02

C++运算符重载_拷贝构造_03

正确代码:

int main() {
try {
Test* p = new Test[5];
cout << "开辟空间后返回的地址:" << p << endl;
// delete p; // 调用析构,不会查看前4个字节确定数组元素的个数
delete[]p; //会查看前4个字节确定数组元素的个数
}
catch(const bad_alloc& err){
cerr << err.what() << endl;
}
return 0;
}

C++运算符重载_c++_04


总结: 自定义的且提供了析构函数的类类型,为了调用正确的析构函数,开辟对象数组的时候,会在对象数组0号元素的上面多开辟4字节,用于存放数组的元素个数。

C++运算符重载_拷贝构造_05


参考:​为什么new/delete和new[]/delete[]必须配对使用?​

4种new

// 可能抛出异常的new(bad_alloc)
int* p1 = new int(10);
// 不抛出异常的new
int* p2 = new (nothrow) int;
// 堆空间生成常量的new
const int* p3 = new const int(10);
// 定位new,在指定内存申请空间,未必是从堆空间申请
int data = 0;
int* p4 = new (&data) int(10);

四、重载new和delete实现对象池

#include<iostream>
#include<string.h>

using namespace std;

template<typename T>

class Queue {
private:
struct QueueItem {
T _data;
QueueItem* _next;
static QueueItem* _item_pool;
static const int POOL_ITEM_SIZE = 100000;
QueueItem(T data = T()) :_data(data), _next(nullptr) {

}

void* operator new(size_t size) {
if (nullptr == _item_pool) {
// 为空表示没有创建对象池或者对象池满
_item_pool = (QueueItem*)malloc(POOL_ITEM_SIZE * sizeof(QueueItem));
QueueItem* p = _item_pool;
for (p; p != _item_pool + POOL_ITEM_SIZE - 1; p++) {
p->_next = p + 1; // 当前节点的next存放下一个节点的地址
}
// 最后一个对象池_next为nullptr
p->_next = nullptr;
}
// 开辟的首地址返回
QueueItem* p = _item_pool;
// 这里调用_next后,_item_pool可能为空
_item_pool = _item_pool->_next;
return p;
}

// 回收节点放在链表头,_item_pool始终指向链表头
void operator delete(void* ptr) {
QueueItem* p = (QueueItem*)ptr;
p->_next = _item_pool;
_item_pool = p;
}

};

QueueItem* _front; // 指向头结点,并不是首元素节点
QueueItem* _rear; // 指向队尾

public:
Queue() {
_front = new QueueItem();
_rear = _front;
}
~Queue() {
QueueItem* cur = _front;
while (cur != nullptr) {
_front = _front->_next;
delete cur;
cur = _front;
}
}
void push(const T& val) {
QueueItem* item = new QueueItem(val);
_rear->_next = item;
_rear = item;
}
void pop() {
if (is_empty()) {
return;
}
QueueItem* tmp = _front->_next;
_front->_next = tmp->_next;

if (_front->_next == nullptr) {
// 此时列表只有一个元素,删了就剩头节点
_rear = _front;
}
delete tmp;
tmp = nullptr;
}
bool is_empty() {
return _front == _rear;
}
T front() const{
return _front->_next->_data;
}


};

template <typename T>
typename Queue<T>::QueueItem* Queue<T>::QueueItem::_item_pool = nullptr;



int main() {
Queue<int> q;

for (int i = 0; i < 100000; i++) {
// 此for循环频繁进程malloc和free,效率低
q.push(i);
q.pop();
}
// 重载new和delete,创建一个对象池,new的时候直接取,delete的时候归还到对象池


cout << q.is_empty() << endl;
return 0;
}

缺点:此代码最后没有手动释放对象池空间,后期学习智能指针后可以进行释放

五、迭代器的失效问题

C++运算符重载_#define_06


迭代器为什么失效?

迭代器不允许一边读一边修改

  1. 当容器调用erase方法后,当前位置到容器末尾元素的所有迭代器全部失效(首元素到插入点的迭代器有效)
  2. 当容器调用insert方法后,当前位置到容器末尾元素的所有迭代器全部失效(首元素到插入点的迭代器有效)
  3. insert来说,如果引起容器内存扩容,原来容器的所有的迭代器就全部失效
  4. 不同容器的迭代器不能进行比较
// 删除所有偶数
void fun1(vector<int>& vec) {
vector<int>::iterator iter = vec.begin();
while (iter != vec.end()) {
if (*iter % 2 == 0) {
iter = vec.erase(iter);
}
else {
iter++;
}
}
}

// 给vec所有偶数前添加一个小1的值
void fun2(vector<int>& vec) {
vector<int>::iterator iter = vec.begin();
while (iter != vec.end()) {
if (*iter % 2 == 0) {
iter = vec.insert(iter, *iter - 1);
++iter;
}
++iter;
}
}

六、不可被重载的运算符

大多数运算符都是可以重载的,但是有5个运算符C++语言规定是不可以重载的

  1. ​.​​(点运算符),通常用于去对象的成员,但是->(箭头运算符),是可以重载的
  2. ​::​​(域运算符),即类名+域运算符,取成员,不可以重载
  3. ​.*​​(点星运算符)不可以重载,成员指针运算符.*,即成员是指针类型
  4. ​?:​​(条件运算符),不可以重载
  5. ​sizeof​​,不可以重载