01 运算符重载
C++的运算符重载:使对象的运算表现的和编译器内置类型一样
template <typename T>
T sum(T a, T b)
{
return a + b; // a.operator+(b) a调用加法函数,把b当实参传入
}
上面代码中的a,b没有指定类型,可以任意实例化类型。如果a + b是普通类型,内置编译器可以自行编译计算,但a和b是对象类型,编译器不知道对象如何相加,要用运算符重载。
复数类的实现例子
class CComplex
{
public:
CComplex(int r = 0, int i = 0)
:mreal(r), mimage(i) {}
CComplex& operator+(const CComplex &src)
{
/*
CComplex comp;
comp.mreal = this->mreal + src.mreal;
comp.mimage = this->mimage + src._mimage;
return comp;
*/
// 优化
return CComplex(this->mreal + src.mreal, this->mimage + src.mimage);
}
void show() { cout << "real:" << mreal << " image:" << mimage<< endl; }
private:
int mreal;
int mimage;
friend CComplex operator+(const CComplex &lhs,const CComplex &rhs);
};
// 全局运算符重载函数
CComplex operator+(const CComplex &lhs,const CComplex &rhs)
{
return CComplex(lhs.mreal + rhs.mreal,lhs.mimage + rhs.mimage);
}
int main()
{
CComplex comp1(10, 10);
CComplex comp2(20, 20);
// comp1.operator+(comp2) 加法运算符的重载函数
// 也可以写成本质的形式 comp1.operator+(comp2)
CComplex comp3 = comp1 + comp2;
comp3.show();
CComplex comp4 = comp1 + 20; // comp1.operator+(20)
comp4.show();
/*
编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法);
如果没有成员方法,就在全局作用域找合适的运算符重载函数。
下面调用了全局运算符重载函数,把整型转成复数类型 ::operator(30,comp1)
*/
CComplex comp5 = 30 + comp1;
comp5.show();
return 0;
}
单目运算符: ++ /- -
operator++():
扩号中无参数,为前置++。
CComplex& operator++()
{
mreal += 1;
mimage += 1;
return *this;
}
operator++(int):
括号中带一个整型参数的,为后置++。
CComplex operator++(int)
{
/*
CComplex comp = *this; // 保留旧值
mreal += 1;
mimage += 1;
return comp; // 返回原来的旧值
以下为优化
*/
return CComplex( mreal++, mimage++ );
}
// CComplex operator++()
comp5 = ++comp1;
// CComplex operator++(int)
comp5 = comp1++;
复合赋值运算符:+=
// 全局作用域下
void operaotr+=(const CComplex &src)
{
mreal += src.mreal;
mimage += src.image;
}
// void comp.operator+=(comp2) ::operator+=(comp1,comp2)
comp1 += comp2;
对象信息的输入和输出
为了使得和内置编译器同样的输入和输出形式
即cin >> comp1;
和 cout << comp1 << endl;
对此提供相应的输入和输出运算符重载函数,都是在全局作用域下实现的,如下
// 输入运算符重载函数
istream& operator>>(istream &in,const CComplex &src)
{
in >> src.mreal >> src.mimage;
return in;
}
// 输出运算符重载函数
ostream& operator<<(ostream &out,const CComplex &src)
{
out << "mreal:" << src.mreal << "mimage:" << src.mimage << endl;
return out;
}
02 Strig类
1 模拟实现C++的string类代码
#include <iostream>
using namespace std;
class String {
public:
// 构造函数
String(const char* p = nullptr)
{
if (p != nullptr)
{
_pstr = new char[strlen(p) + 1];
strcpy(_pstr, p);
}
else
{
_pstr = new char[1];
*_pstr = '\0';
}
}
// 析构函数
~String()
{
delete[]_pstr;
_pstr = nullptr;
}
// 拷贝构造函数
String(const String& str)
{
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
}
// 赋值运算符重载
String& operator=(const String& str)
{
if (this == &str)
return *this;
delete[]_pstr;
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
return *this;
}
// 大于运算符重载
bool operator>(const String& str)const
{
return strcmp(_pstr, str._pstr) > 0;
}
// 小于运算符重载
bool operator<(const String& str)const
{
return strcmp(_pstr, str._pstr) < 0;
}
// 等于运算符重载
bool operator==(const String& str)const
{
return strcmp(_pstr, str._pstr) == 0;
}
// 返回字符串长度
int lengh()const { return strlen(_pstr); }
// 返回C字符串
const char* c_str()const { return _pstr; }
// 下标运算符重载
//char ch = str6[6]; str6[6] = '7'
char& operator[](int index) { return _pstr[index]; }
//char ch = str6[6]; 不允许修改!str6[6] = '7'
const char& operator[](int index)const { return _pstr[index]; }
private:
char* _pstr;
friend String operator+(const String& lhs, const String& rhs);
friend ostream& operator<<(ostream& out, const String& str);
};
// 加法运算符重载
String operator+(const String& lhs, const String& rhs)
{
char* ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
strcpy(ptmp, lhs._pstr);
strcat(ptmp, rhs._pstr);
String result(ptmp);
delete[] ptmp; // 释放临时分配的内存
return String(ptmp);
}
// 输出流运算符重载
ostream& operator<<(ostream& out, const String& str)
{
out << str._pstr;
return out;
}
int main() {
String str1;
String str2 = "aaa"; // string(const char*)
String str3 = "bbb";
String str4 = str2 + str3;
String str5 = str2 + "ccc";
String str6 = "ddd" + str2;
cout << "str6:" << str6 << endl;
if (str5 > str6)
{
cout << str5 << ">" << str6 << endl;
}
else
{
cout << str5 << "<" << str6 << endl;
}
int len = str6.length();
for (int i = 0; i < len; ++i)
{
cout << str6[i] << " ";
}
cout << endl;
// string -> char*
char buf[1024] = { 0 };
strcpy(buf, str6.c_str());
cout << "buf:" << buf << endl;
return 0;
}
上面代码中的加法运算符重载函数效率低,含有两次的内存构造和释放,对此改为只有一次的内存构造和释放
String operator+(const String& lhs, const String& rhs) {
String tmp;
tmp._pstr = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
strcpy(ptmp, lhs._pstr);
strcat(ptmp, rhs._pstr);
return String(ptmp);
}
2 string字符串对象的迭代器iterator实现
优化效率
#include <iostream>
using namespace std;
class String {
public:
// 构造函数
String(const char* p = nullptr) {
if (p != nullptr) {
_pstr = new char[strlen(p) + 1];
strcpy(_pstr, p);
}
else {
_pstr = new char[1];
*_pstr = '\0';
}
}
// 析构函数
~String() {
delete[]_pstr;
_pstr = nullptr;
}
// 拷贝构造函数
String(const String& str) {
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
}
// 赋值运算符重载
String& operator=(const String& str) {
if (this == &str)
return *this;
delete[]_pstr;
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
return *this;
}
// 大于运算符重载
bool operator>(const String& str)const {
return strcmp(_pstr, str._pstr) > 0;
}
// 小于运算符重载
bool operator<(const String& str)const {
return strcmp(_pstr, str._pstr) < 0;
}
// 等于运算符重载
bool operator==(const String& str)const {
return strcmp(_pstr, str._pstr) == 0;
}
// 返回字符串长度
int lengh()const { return strlen(_pstr); }
// 返回C字符串
const char* c_str()const { return _pstr; }
// 下标运算符重载
//char ch = str6[6]; str6[6] = '7'
char& operator[](int index) { return _pstr[index]; }
//char ch = str6[6]; 不允许修改!str6[6] = '7'
const char& operator[](int index)const { return _pstr[index]; }
//给String字符串类型提供迭代器的实现
class iterator
{
public:
iterator(char* p = nullptr) :_p(p){}
bool operator!=(const iterator& it) {
return _p != it._p;
}
void operator++()
{
++_p;
}
char& operator*() { return *_p; }
private:
char* _p;
};
//begin返回的是容器底层首元素的迭代器的表示
iterator begin() { return iterator(_pstr); }
//end返回的是容器末层元素后继位置的迭代器的表示
iterator end() { return iterator(_pstr + lengh()); }
private:
char* _pstr;
friend String operator+(const String& lhs, const String& rhs);
friend ostream& operator<<(ostream& out, const String& str);
};
// 加法运算符重载
String operator+(const String& lhs, const String& rhs) {
// char* ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
String tmp;
tmp._pstr = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
strcpy(ptmp, lhs._pstr);
strcat(ptmp, rhs._pstr);
// delete[] ptmp; // 释放临时分配的内存
return String(ptmp);
}
// 输出流运算符重载
ostream& operator<<(ostream& out, const String& str) {
out << str._pstr;
return out;
}
int main() {
//迭代器的功能:提供一种统一的方式,来透明的遍历容器
String str1 = "hello world!";
// 容器迭代器的类型
String::iterator it = str1.begin();
for (; it != str1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//以C++11 foreach的方式来遍历容器的内部元素的值=>底层,还是通过迭代器进行遍历的
for (char ch : str1) {
cout << ch << " ";
}
cout << endl;
}
03 迭代器的失效问题
迭代器失效问题
一. 迭代器为什么会失效?
1. 当容器调用rease方法后,当前位置到容器末尾元素的所有的迭代器全部失效了
2. 当容器调用insert方法后,当前位置到容器末尾元素的所有的迭代器全部失效了
迭代器依然有效 迭代器全部失效
首元素 -> 插入点/删除点 -> 末尾元素
3. insert来说,如果引起容器内存扩容
原来容器的所有的迭代器就全部失效了
首元素 -> 插入点/删除点 -> 末尾元素
4. 不同容器的迭代器是不能进行比较运算的
二. 迭代器失效以后,问题该怎么解决?
对插入/删除点的迭代器进行更新操作
int main() {
vector<int> vec; // 向量
for (int i = 0; i < 20; ++i) {
vec.push_back(rand() % 100 + 1); // push_back() 在Vector最后添加一个元素
}
for (int v : vec) {
cout << v << " ";
}
cout << endl;
for (int v : vec)
{
cout << v << " ";
}
cout << endl;
return 0;
}
给vec容器所有的偶数前面添加一个偶数值小于1的数字
auto it = vec.begin();
for (; it != vec.end(); ++it) {
if (*it % 2 == 0) {
vec.insert(it, *it - 1);
break;
}
}
修改后的代码
给vec容器所有的偶数前面添加一个偶数值小于1的数字
auto it = vec.begin();
for (; it != vec.end(); ++it) {
if (*it % 2 == 0) {
it = vec.insert(it, *it - 1);
++it;
//break;
}
}
把vec容器中所有的偶数全部删除
auto it = vec.begin();
for (; it != vec.end(); ++it) {
if (*it % 2 == 0) {
//迭代器失效的问题,第一次调用erase以后,迭代器it就失效了
vec.erase(it); // insert(it, val) erase(it)
//break;
}
}
修改后的代码
把vec容器中所有的偶数全部删除
auto it = vec.begin();
while ( it != vec.end())
{
if (*it % 2 == 0)
{
it = vec.erase(it); // insert(it, val) erase(it)
//break;
}
else
{
++it;
}
}
for (int v : vec)
{
cout << v << " ";
}
cout << endl;
04
new和delete
-
malloc和new的区别?
a. malloc按字节开辟内存的;new开辟内存时需要指定类型 new int[10]
所以malloc开辟内存返回的都是void* operator new -> int*
b. malloc只负责开辟空间,new不仅仅有malloc的功能,可以进行数据的初始化new int(20); new int[20](); int()
c. malloc开辟内存失败返回nullptr指针;new抛出的是bad_alloc类型的异常
-
free和delete的区别?
delete (int*) p:先调用析构函数;再free(p)
new和delete能混用吗?C++为什么区分单个元素和数组的内存分配和释放呢?
new delete
new[] delete[]
对于普通的编译器内置类型 new/delete[] new[]/delete
自定义的类类型,有析构函数,为了调用正确的析构函数,那么开辟对象数组的时候,
会多开辟4个字节,记录对象的个数
// 先调用operator new开辟内存空间,然后调用对象的构造函数(初始化)
void* operator new(size_t size)
{
void* p = malloc(size);
if (p == nullptr)
throw bad_alloc();
return p;
}
// delete p; 调用p指向对象的析构函数,再调用operator delete施放内存空间
void operator delete(void* ptr)
{
free(ptr);
}