C++仿写String类与返回值

文章讨论了C++类`MyString`和`Complex`的实现,包括构造函数、析构函数、拷贝构造函数、移动构造函数以及赋值运算符重载。特别关注了运算符重载的返回值问题,指出返回局部变量的引用或指针是错误的,并提供了四种不同的加法运算符重载方法,分析了它们的效率和适用场景。文章强调了正确处理对象生命周期和返回值类型的重要性,以避免悬挂引用和野指针的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

15.1MyString实现

15.2值、指针、引用与返回值

15.3运算符重载之返回值问题

15.3.1主函数第一种方案

15.3.2主函数第二种方案

15.4总结


15.1MyString实现

class MyString
{
private:
    char* pstr;
public:
    MyString(const char* p=nullptr)
    {
        if (p != nullptr)
        {
            int len = strlen(p) + 1;
            pstr = new char[len];
            strcpy_s(pstr, len, p);
        }
        else
        {
            pstr = new char[1];
            *pstr = '\0';
        }
        cout << "Create MyString  " << this << endl;
    }
    ~MyString()
    {
        delete[]pstr;//在删除时不需要判空,因为delete和free内部存在判空
        cout << "destroy MyString  " << this << endl;
    }
    MyString(const MyString& s):pstr(nullptr)
    {
        int len = strlen(s.pstr) + 1;
        pstr = new char[len];
        strcpy_s(pstr, len, s.pstr);
        cout << &s << "  copy MyString  " << this << endl;
    }
    MyString(MyString&& s)//移动拷贝构造
    {
        pstr = s.pstr;
        s.pstr = nullptr;
        cout << &s << "  move copy create  " << this << endl;
    }
    MyString& operator=(const MyString& s)
    {
        if (this != &s)
        {
            int len = strlen(s.pstr) + 1;
            delete[]pstr;
            pstr = new char[len];
            strcpy_s(pstr, len, s.pstr);
            cout << &s << " operator= " << this << endl;
        }
        return *this;
    }
    MyString& operator=(MyString&& s)//移动赋值运算符重载
    {
        if (this != &s)
        {
            delete[]pstr;
            pstr = s.pstr;
            s.pstr = nullptr;
            cout << &s << "  move operator=  " << this << endl;
        }
        return *this;
    }
    
    void Print()const
    {
        if (pstr != nullptr)
        {
            cout << "string:" << pstr << endl;
        }
    }
};

15.2值、指针、引用与返回值

class Complex
{
private:
    int real;
    int image;
public:
    Complex(int r=0,int i=0):real(r),image(i)
    {
        cout<<"Create Complex "<<this<<endl;
    }
    ~Complex()
    {
    	cout<<"Destroy Complex "<<this<<endl;    
    }
    Complex(const Complex & x):real(x.real),image(x.image)
    {
        cout<<"Copy Complex "<<this<<endl;
    }
    Complex& operator=(const Complex& x)//赋值运算符重载函数
    {
        if(this==&x)return *this;//防止出现自赋值情况
        real=x.real;
        image=x.image;
        cout<<this<<" operator= "<<&x<<endl;
        return *this;
    }
    void Print()
    {
        cout << "real:" << real << " image:" << image << endl;
    }
};
Complex get_a()
{
    Complex tmp(2,3);
    return tmp;
}
Complex& get_b()//错误的方式,但可以运行
{
    Complex tmp(2,3);
    return tmp;
}
Complex* get_c()//错误的方式,但可以运行
{
    Complex tmp(2,3);
    return &tmp;
}
int main()
{
    Complex c1=get_a();
    Complex& c2=get_b();
    Complex* p=get_c();
    c1.Print();
    c2.Print();
    p->Print();
    return 0;
}

对于指针在函数结束时会拿到函数中局部变量的地址,当函数结束栈帧被系统回收释放,但指针依然指向被系统回收那块空间,读取里面存放的数据就是随机值。引用在底层也是指针,所以原因相同。

什么时候可以以引用或指针作为函数的返回值:

答:当对象的生存期不受函数影响(即函数结束该对象依然存在),就可以以引用或者指针返回其地址。比如:全局变量,静态变量,堆区变量等。

Complex& fun(Complex& x)//返回引用参数也可以
{
	return x;
}

15.3运算符重载之返回值问题

class Complex
{
private:
	int real;
	int image;
public:
	Complex(int r = 0, int i = 0) :real(r), image(i)
	{
		cout << "Create Complex " << this << endl;
	}
	~Complex()
	{
		cout << "Destroy Complex " << this << endl;
	}
	Complex(const Complex& x) :real(x.real), image(x.image)
	{
		cout << &x << "  Copy Create Complex  " << this << endl;
	}
	Complex& operator=(const Complex& x)
	{
		if (this != &x)
		{
			real = x.real;
			image = x.image;
			cout << &x << "  operator=  " << this << endl;
			return *this;
		}
	}
	void Print() const 
	{ 
		cout << "Real: " << real << "Image: " << image <<"  "<< this << endl;
	}
    //加法运算符重载函数有四种方法:
    //方法一:返回值为值类型
	Complex operator+(const Complex& c)const
	{
		Complex tmp(real + c.real, image + c.image);
		return tmp;
	}
    //方法二:返回值为一引用类型,这种存在问题
	Complex& operator+(const Complex& c)const
	{
		Complex tmp(real + c.real, image + c.image);
		return tmp;
	}
    //方法三:以值的方式直接返回构造函数构造的不具名将亡值对象
	Complex operator+(const Complex& c)const
	{
		return Complex(real + c.real, image + c.image);
	}
    //方法四:error,以引用的方式直接返回构造函数构造的不具名将亡值,无法编译通过
    Complex& operator+(const Complex& c)const
	{
		return Complex(real + c.real, image + c.image);
        //error C2440: “return”: 无法从“Complex”转换为“Complex &”
        // message : 非常量引用只能绑定到左值
	}
};
//主函数存在两种方案:
//第一种,定义c3对象之后再对c3进行赋值
int main()
{
    Complex c1(1, 2), c2(2, 3);
	Complex c3;
	c3 = c1 + c2;
    return 0;
}
//第二种,定义c3对象时并对c3进行赋值
int main()
{
    Complex c1(1, 2), c2(2, 3);
	Complex c3 = c1 + c2;
    return 0;
}

15.3.1主函数第一种方案

使用方法一:

 使用方法二:

 使用方法三:

 

此处的无名对象就是将亡值

15.3.2主函数第二种方案

使用方法一:

使用方法二:

 使用方法三:

 

15.4总结

第一种运算符重载方法在使用过程中会构造两个对象,一个是局部对象tmp,另一个是将亡值对象,多次的构造和销毁对象会造成程序效率降低;而方法二只构建一个tmp对象,并将其以引用形式返回,这样存在问题,就是不能将局部变量以引用或者指针的形式返回(在15.2中介绍)。但第三种方法在使用过程中就只构建一次对象。针对主函数的方案不同,第一种是先构造对象,再进行赋值,这样会调用调用构造函数和赋值运算符重载函数,第二种是构建并进行初始化,此处会直接调用拷贝构造函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值