如何正确的重载类类型中char*强制类型转换符?

重载类类型强制转换符时的注意事项

以将类类型强制转换为char*类型为例,我们都知道强制转换为char*类型,无非就是重载operator char*(),但是我们经常没注意的是,当我们进行了如下编程,会出现出乎意料的现象(错误示例):

#include <iostream>  
using namespace std;  
#include <sstream>  
#include <string>  
  
class Person  
{  
private:  
    string name;  
    int age;  
public:  
    Person() = default;  
    Person(string name, int age);  
    Person(Person& obj);  
  
    operator char*();  
  
    ~Person() = default;  
};  
  
Person::Person(string name, int age)  
{  
    this->name = name;  
    this->age = age;  
}  
  
Person::Person(Person & obj)  
{  
    this->name = obj.name;  
    this->age = obj.age;  
}  
  
Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    return const_cast<char*>(sstr.str().c_str());  
}  
  
int main()  
{  
    Person Person_Obj1("张三", 19);  
    cout << Person_Obj1 << endl;  
}  

输出结果:(全是乱码)

 

我们探究一下原因:

导致这样的原因,是因为我们返回的是一个指向暂时内存空间的char*指针,也就是说当我们将返回的char*类型的指针(注:当我们重载了char*强制转换符,类类型对象在cout输出时会隐式转换为char*变量进行输出)进行cout输出之前,char*类型指针指向的内存空间早已被释放掉,如下示例:

#include <iostream>  
using namespace std;  
#include <sstream>  
#include <string>  
  
char* ShowInf()  
{  
    ostringstream sstr("张三");  
    return const_cast<char*>(sstr.str().c_str()); // 返回指向临时存储空间的char*型指针  
}  
  
int main()  
{  
    cout << ShowInf() << endl;  // 输出之前伴随着ShowInf()函数生命的结束里面的临时变量已经被销毁
}  

输出结果:(输出乱码)

 

因此,我们对重载程序做如下改进:

我们在类类型中添加一个新的变量,这个变量的类型和我们要重载的强制类型转换符的返回值一致,即

我要重载char*类型的强制类型转换符,那我就在类类型中定义一个专门定义一个char*的变量,以供存储被强制类型转换后的结果。

#include <iostream>  
using namespace std;  
#include <sstream>  
#include <string>  
  
class Person  
{  
private:  
    string name;  
    int age;  
    char* Output;   
public:  
    Person() = default;  
    Person(string name, int age);  
    Person(Person& obj);  
  
    operator char*();  
  
    ~Person() = default;  
};  
  
Person::Person(string name, int age)  
{  
    this->name = name;  
    this->age = age;  
}  
  
Person::Person(Person & obj)  
{  
    this->name = obj.name;  
    this->age = obj.age;  
}  
  
Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    this->Output = new char[sstr.str().size()];  
    strcpy(this->Output, sstr.str().c_str());  
    return this->Output;  
}  
  
int main()  
{  
    Person Person_Obj1("张三", 19);  
    cout << Person_Obj1 << endl;  
}  

输出结果:

 

我们看到输出结果正常,我们对比“改进前”与“改进后“ operator char*()中代码的区别:

改进前:

Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    return const_cast<char*>(sstr.str().c_str());  // 返回指向临时存储区域的指针(临时变量其实就是在栈(stack)当中存储的变量,它会随着局部变量堆在函数的释放而被释放掉)
}  

 

改进后:

Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    this->Output = new char[sstr.str().size()];  // 动态申请内存空间,将数据存在堆区(heap)中
    strcpy(this->Output, sstr.str().c_str());  // 将临时变量的值拷贝至堆区变量当中
    return this->Output;  // 输出堆区变量
}  

因此,我们以后切记:重载强制类型转换符所得到的相应类型的数据应该在本类类型中存放,不应该返回临时变量,任何返回临时变量的操作都是不可行的。

再次改进版本:将申请的内存空间进行释放:

#include <iostream>  
using namespace std;  
#include <sstream>  
#include <string>  
  
class Person  
{  
private:  
    string name;  
    int age;  
    char* Output;   
public:  
    Person() = default;  
    Person(string name, int age);  
    Person(Person& obj);  
  
    explicit operator char*();  
  
    void Cout();  
  
    ~Person();  
};  
  
Person::~Person()  
{  
    if (this->Output != nullptr) // 防止再次被释放引发错误  
    {  
        delete[] this->Output;  
    }  
}  
  
Person::Person(string name, int age)  
{  
    this->name = name;  
    this->age = age;  
}  
  
Person::Person(Person & obj)  
{  
    this->name = obj.name;  
    this->age = obj.age;  
}  
  
Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    this->Output = new char[sstr.str().size() + 1]; // 申请内存空间的时候一定要多预留一个空间  
    strcpy(this->Output, sstr.str().c_str());  
    return this->Output;  
}  
  
void Person::Cout()  
{  
    cout << static_cast<char*>(*this) << endl;  
    delete[] this->Output;  
    this->Output = nullptr;  
}  
  
int main()  
{  
    Person Person_Obj1("张三", 19);  
    Person_Obj1.Cout();  
} 

 

在代码当中,我做了如下改进:

构造了一个输出函数:

void Person::Cout()  
{  
    cout << static_cast<char*>(*this) << endl;  
    delete[] this->Output;  
    this->Output = nullptr;  
}

这个函数可以进行自定义输出,而且我们还可以回收动态申请的内存空间。

Person::~Person()  
{  
    if (this->Output != nullptr) // 防止再次被释放引发错误  
    {  
        delete[] this->Output;  
    }  
} 

在析构函数中一定要包含这段代码,防止已经被释放的内存再次被释放。

 explicit operator char*();

​​​​​​​  

我将重载的char*强制类型转换符声明为explicit类型的,已告知系统不要进行隐式类型转换,这样操作可以防止隐式类型转换时this->Output申请内存空间造成内存泄漏。

在操作过程中,我遇到了如下问题:

CRT detected that the application wrote to memory after end of heap buffer

这句话告诉我们的是:你释放的内存空间超过了他本身的长度,也就是说你的char类型数组最后一个元素不是’\0’导致了,释放内存的空间没有了尽头从而导致报错,因此我做了如下改进:

Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    this->Output = new char[sstr.str().size() + 1]; // 申请内存空间的时候一定要多预留一个空间  
    strcpy(this->Output, sstr.str().c_str());  
    return this->Output;  
}  

我多给this->Output多预留了一个申请空间用于放置’\0’字符型数组结束标志,在为改进的案例当中,我显然没有这样做,我的为改进代码如下:

Person::operator char*()  
{  
    ostringstream sstr;  
    sstr << this->name << "的年龄为" << this->age;  
    this->Output = new char[sstr.str().size()];  
    strcpy(this->Output, sstr.str().c_str());  
    return this->Output;  
}  

我们疑问:为什么这段代码没有事情呢?

因为我们的析构函数并没有释放掉this->Output所指向的内存空间,为什么呢?

因为我没定义用于释放this->Output指向的内存空间的析构函数~Person(),我使用了系统默认的析构函数”~Person=default”,因此这段代码中动态申请的内存空间会随着整个程序的结束而被释放掉。

我们又疑问:为什么释放掉整个程序的时候不会造成“过释放”内存的错误呢?

因为我们的错误造成的原因是我们释放的内存空间触碰到了这个程序中其他存储单元的利益,因此程序会报错,但是当我们把整个程序全部释放掉的时候就不存在谁侵犯谁的利益问题了,反正全部的利益(所占内存)全部被释放了。

猜你喜欢

转载自blog.csdn.net/weixin_45590473/article/details/112252878