更多的重载
重载类型转换
重载内存分配释放
重载递增递减
重载类型转换
实现类型转变运算符
类型转换运算符只能重载为类的成员函数
类型转换运算符没有返回值,他的返回值由类型转换的类型来决定
语法:operator 类型()const
hstring::operator int()
{
int val{ 0 };
//int i 标尺
//cstr[0]='-',i=1; cstr[0]!='-',i=0
int i = cstr[0] == '-';
while (cstr[i] >= '0' && cstr[i] <= '9')
{
val = val * 10 + cstr[i++] - 48;
}
//val=val*((表达式)*2-1),在有负号时表达式为0,无负号时表达式为1
val = val * ((cstr[0] != '-') * 2 - 1);
return val;
}
int main()
{
hstring str{ "123" };
str = str + 456;
int x = (int)str;
std::cout << x;//123456
}
解决隐式类型转换带来的多义性问题
在类型转换重载函数前加 explicit关键字可以限制该函数禁止使用隐式类型转换(C++ 11)
(但可以强制类型转换)
explicit operator int();
问题背景
class T
{
public:
T(int val)
{}
operator int(){
return 1;
}
int operator+(T x){
return 2;
}
};
int main()
{
T t1{ 100 }, t2{ 200 };
int x = t1 + 100;//报错
//有歧义,t1可以隐式类型转换为int,x=101
//也可以是100类型转换为T,x=102
}
使用explicit
class T
{
public:
T(int val)
{}
explicit operator int()//explicit
{
return 1;
}
int operator+(T x){
return 2;
}
};
int main()
{
T t1{ 100 }, t2{ 200 };
int x = t1 + 100;
//不允许t1隐式类型转换成int,所以100转换成T类
std::cout<<x;//2
int y=(int)t1+100;//可以强制类型转换
std::cout<<t1;//1
}
explicit operator int()
{...}
hstring str{-123};
int x=str+123;//报错
//因为 str+123 是hstring类型的,而operator int()被explicit修饰,即hstring不能隐式类型转换为int
int x=(int)(str+123);//可以强制类型转换
std::cout<<x;//-123123
int x=(int)(str)+123;//-123+123=0
std::cout<<x;//0
bool类型与void*的选择
某些时候,我们为了表示某个类里的指针是否为空,可以重载该类的void*类型来实现如下操作
if(p)
if(!p)
if(p!=NULL)
if(p!=nullptr)
但是其实也可以实现重载bool类型转换来实现该操作,但是我们一般不推荐重载bool类型
hstring::operator void* ()
{
return cstr;
}
int main()
{
hstring str{ 123 };
if (str == nullptr)std::cout << "空字符串";
else std::cout << "非空字符串";
}
重载内存分配释放
new和delete的工作原理
new的工作原理
Role*user=new Role();
通过new分配内存空间 (能接管)
调用构造函数
返回指针
delete user;
调用析构函数
通过delete释放内存 (能接管)
重载内存分配
内存分配在C++中是比较重要的内容,有的时候我们需要重载内存分配和释放,大部分时候是为了解决内存碎片的问题,
new的六种形式
//new的六种形式:
void* operator new (size_t size);//size_t就是unsigned,size代表你要分配的内存的大小
void* operator new[](size_t size);
void* operator new(size_t size,const std:nothrow_t&) noexcept;
void* operator new[](size_t size,const std:nothrow_t&) noexcept;
//禁止重载的两种形式
void* operator new(size__t size,void* p) noexcept;//可以指定内存分配到这块区域
void* operator new[](size_t size,void* p) noexcept;
blut shota = new ((void*)0x20000) bult[10];
char* mem=new char[0x1000];
int main()
{
blut* shota = new (mem) blut[10];
std::cout<<(void*)mem<<std::endl;//0175A578
std::cout<<shota<<std::endl;//0175A578
delete[] (mem);
}
void* blut::operator new(size_t size)
{
return new blut;//死循环,因为new blut继续调用void* operator new(size_t size)
}
——————————————————————————————————————————————————————————————————————————————————————————
void* blut::operator new(size_t size)
{
return ::operator new (size);//加上全局符号,调用的是默认的那个new
}
重载内存释放
内存分配在C++中是比较重要的内容,有的时候我们需要重载内存分配和释放,大部分时候是为了解决内存碎片的问题,
delete的六种形式:
void operator delete (void* adr) noexcept;
void operator delete[](void* adr) noexcept;
void operator delete(void* adr,const std:nothrow_t&) noexcept;
void operator delete[](void* adr,const std:nothrow_t&)noexcept;
void operator delete(void* adr,void* p) noexcept;
void operator delete[](void+ adr,void* p) noexcept;
class blut
{
public:
float x;
float h;
float y;
float damage;
void* operator new(size_t size);
void operator delete(void* adr) noexcept;
};
/*
void* blut::operator new(size_t size)
{
return new blut;//死循环
}
*/
char* mem = new char[0x10000];
void* blut::operator new(size_t size)
{
return mem;
}
void blut::operator delete(void* adr) noexcept
{
}
int main()
{
blut* shota = new blut;
shota->x = 100;
delete shota;
}
小细节
void* blut::operator new(size_t size)其实是static类型,所以没有this指针
因为new的时候还在分配内存空间,没有自己的内存空间也就没有this指针
void blut::operator delete(void* adr) noexcept也是static类型,也没有this指针
因为在使用delete之前已经调用析构函数,内存空间已经释放
删除不想用的new和delete版本
删除系统默认函数
class blut
{
public:
float x;
float h;
float y;
float damage;
void* operator new(size_t size);
void operator delete(void* adr) noexcept;
void* operator new[](size_t size) = delete;//删除
void operator delete[](void* adr) = delete;//删除
};
重载带有额外参数的new和delete
为了方便和提高灵活性,可以重载带有额外参数的new和delete版本
void* operator new(size_t size.const char* meminfo);
void*operator delete(void*adr,const char* meminfo) noexcept;
但是带有额外参数的delete版本不能手动调用, 只有在构造函数发生异常的时候, 系统会自动调用
void* blut::operator new(size_t size,const char* txt)
{
std::cout << txt;
return mem;
}
//要写相对应的delete版本
void blut::operator delete(void* adr, const char* txt) noexcept
{
}
int main()
{
blut* shota = new ("test") blut;
delete () shota;//报错,所以还要准备一个普通版本的delete
}
————————————————————————————————————————————————————————————————————————————————————————
void* blut::operator new(size_t size,const char* txt)
{...}
void blut::operator delete(void* adr, const char* txt) noexcept
{...}
void blut::operator delete(void* adr) noexcept//普通版本的delete
{...}
int main()
{
blut* shota = new ("test") blut;
delete shota;
}
class blut
{
bool bUse = true;
public:
float x;
float h;
float y;
float damage;
~blut();
void* operator new(size_t size,const char* txt);
void operator delete(void* adr, const char* txt) noexcept;
void operator delete(void* adr) noexcept;
void* operator new[](size_t size) = delete;
void operator delete[](void* adr) = delete;
};
char* mem = new char[1000 * sizeof(blut)]{};//记得将内存初始化为0
void* blut::operator new(size_t size,const char* txt)
{
blut* dat = (blut*)mem;
for (int i = 0; i < 1000; i++)
{
if (!dat[i].bUse)return &dat[i];
}
}
void blut::operator delete(void* adr, const char* txt) noexcept
{
}
void blut::operator delete(void* adr) noexcept
{
//static类型的函数,不能将bUse设置成false
//在析构函数里设置
//delete函数的工作原理:先调用析构函数,然后使用delete释放内存
}
blut::~blut()
{
bUse = false;
}
int main()
{
blut* shota1 = new ("test") blut;
blut* shota2 = new ("test") blut;
blut* shota3 = new ("test") blut;
delete shota1;
blut* shota4 = new ("test") blut;//放在shota1的内存里
delete shota2;
blut* shota5 = new ("test") blut;//放在shota2的内存里
std::cout << shota1 << std::endl;//010AECA0
std::cout << shota2 << std::endl;//010AECB4
std::cout << shota3 << std::endl;//010AECC8
std::cout << shota4 << std::endl;//010AECA0
std::cout << shota5 << std::endl;//010AECB4
}
重置递增递减运算符
重载前置++/--
前置++/--可以重载为类的方法或者是全局函数
方法:
返回类型 operator++();
返回类型 operator--();
全局函数
返回类型 operator++(操作数类型 操作数);
返回类型 operator--(操作数类型 操作数)
class hint
{
char* mem[4];
public:
hint(int val=0);
~hint();
operator int();
void operator=(int val);
hint& operator++();//前置++ (类的方法)
};
hint& operator--(hint& val);//前置-- (全局函数)
hint& hint::operator++()//前置++,先+1后进行运算
{
*this = *this + 1;
return *this;
}
hint& operator--(hint& val)//前置--(全局函数)
{
val = val - 1;
return val;
}
后置+/--
后置++/--可以重载为类的方法或者是全局函数
方法:
返回类型 operator++(int); //int参数没有意义,就是用来区分前置++和后置++
返回类型 operator--(int);
全局函数
返回类型 operator++(操作数类型 操作数, int);
返回类型 operator--(操作数类型 操作数, int);
hint& hint::operator++(int)
{
hint val = *this;
*this = *this + 1;
return val;
}
/*使用时报错
因为hint val=*this;会调用编译器默认的构造函数hint(hint& val);
于是构造函数进行纯粹的复制
mem[0] = new char;
mem[1] = new char;
mem[2] = new char;
mem[3] = new char;
而函数结束时val调用析构函数
hint::~hint()
{
delete mem[0];
delete mem[1];
delete mem[2];
delete mem[3];
}
指针被释放,再使用时就会报错
*/
————————————————————————————————————————————————————————————————————————————————————————
hint& hint::operator++(int)
{
hint val = (this)*this;//调用hint (int val)构造函数
*this = *this + 1;
return val;
}
/*仍然出错
比如说a++
return val的之后a=val,会调用hint (hint& val),同样直接复制地址
而函数删除的时候调用析构函数,同上,内存被释放
a使用的时候就会报错
*/
————————————————————————————————————————————————————————————————————————————————————————
//解决方法:自己写一个hint (hint& val)
hint::hint(hint& val) :hint((int)val)
//委托hint(int val)构造,需要将该构造函数hint类型的val转换成int类型
{}