C++运算符重载

一、实现复数运算符

#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;
}

拷贝构造 :已存在对象和正在生成对象之间的操作
等号运算符 :已经存在的对象之间的操作
在这里插入图片描述
简单运算符重载

	// 这里不可返回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

mallocnew的区别
  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)

freedelete的区别
  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);
}

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

对于基本数据类型而言,newdelete的功能就是mallocfree的功能,也可以混用deletedelete[]

	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;
}

在这里插入图片描述

在这里插入图片描述正确代码:

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;
}

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

在这里插入图片描述
参考: 为什么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;
}

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

五、迭代器的失效问题

在这里插入图片描述
迭代器为什么失效?

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

  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,不可以重载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值