operator=为什么要返回一个reference to *this

本文探讨了C++中函数返回值的概念,并深入分析了operator=运算符重载时返回引用的重要性。通过具体示例说明了返回引用可以提高效率并支持特定的赋值操作。

一、 函数返回值

说到operator=的返回值,那么先必须要说一下函数的返回值。函数的返回值用于初始化在调用函数处创建的临时对象,函数的返回值分值类型和引用类型。

1、 返回值类型

      返回值类型, 即告诉编译器在调用函数处创建的临时对象是函数返回对象的一个值类型。

      Test Fun() {

            Test a;

            return a;

      }

      Test b = Fun();

      则经过编译器处理后伪代码大致如下:

      Fun()里return a;调用拷贝构造生成一个临时Test对象termporary;

      Test b = termporary;  ------这里再用termporary通过拷贝构造来初始化a

      termporary.~Test();---------释放掉此临时对象

      或者经过编译器优化为:

      Test b = Fun();  -------这里Fun里return a的时候没有生成一个临时的Test对象,而是直接通过拷贝构造函数将a初始化对象实例b。


      如果只是单纯调用Fun()函数,如:

      Fun();

      则return a;的时候会调用拷贝构造函数生成一个临时Test对象,函数调用结束就会掉此临时对象的析构函数将它释放掉。


2、返回引用类型

     返回引用类型, 即告诉编译器在调用函数处创建的临时对象是函数返回对象的一个引用。

     Test& Fun() {

            static Test a;
            return  a;

      }

     Test b = Fun();

     则经过编译器处理后伪代码大致如下:

     Fun();  -------return  a;的时候创建Fun的返回对象实例a的引用termporary,即Test& termporary = a;

     Test b = termporary;  ------这里再用termporary通过拷贝构造来初始化b

    

     但是如果通过形如Test& a = Fun();来调用函数呢?经过编译器处理后伪代码大致如下:

     Fun();  -------return  a;的时候创建Fun的返回对象实例a的引用termporary,即Test& termporary = a;

     Test &b = termporary;  ------b为termporary的引用,即为Fun的返回对象实例a的别名

    注:

   函数返回引用类型,其实就是返回了一个真实对象的一个别名(返回的对象termporary就是真实对象的一个别名)


二、operator=返回值

      <<Effective C++>>中说到 operator=必须要返回一个reference to *this,为什么呢? 他给出的理由是 Test a, b, c; a = b = c;  为了实现这种“连锁赋值”。 那么真的如书上所说的如果不返回引用类型就无法实现上述“连锁赋值”吗? 其实不然。

class Test
{
public:
    Test() {  }; 
    Test(int i) { number = i; }; 
    ~Test( ) { }; 
    Test(const Test& p){ number = p.number; };
    Test operator=(const Test& p) {number = p.number; return *this;};  

public:
    void DoSomething() {cout << "My test" << endl; };
public:
    int number;
};
如上代码,Test 的operator=返回的不是引用类型,但是照样能实现a = b = c;这种“连锁赋值”。只是比operator=返回的是引用类型情况下多调用两次copy构造函数。 a = b = c;被解析为 a = (b = c), 那么b = c时返回值必须通过copy构造函数来初始化临时对象termporary。 然后termporary通过操作符号operator=赋值给a,即a = termporary, operator=返回时同样会调用copy构造函数来初始化另外一个临时对象。故多调用两次copy构造函数而已。

所以,只是说从效率上来说, operator=返回引用比返回值类型要高效。

另外, 如果想要支持另外一种“连锁赋值”,如 (a = b) = c; 那么就必须要operator=返回引用类型。即 a = b 返回的是 a的一个引用类型临时对象termporary,那么将c赋值给这个a的引用,即可正确地实现表达式的含义。 如果operator=返回值类型呢? 那么a = b 返回的是a的一个值类型临时对象termporary, 那么c赋值给这个临时对象termporary后,根本没有改变a的值,也就没有正确地实现表达式的含义。

所以,综上两点, operator=还是老老实实地返回一个reference to *this吧。


template <typename T> class sptr { friend class wptr<T>; public: sptr(); ~sptr(); /** * @brief Constructor with specified object to be managed. * * @note Null sptr will be created if `other` is a `nullptr`. * @param other Object to be managed by wptr. */ sptr(T *other); /** * @brief Copy Constructor for sptr with different managed class type(T). * * @param other Input sptr object. */ sptr(const sptr<T> &other); /** * @brief Move constructor. * * @note `other` will be set to a null sptr. * @param other Input sptr object. */ sptr(sptr<T> &&other); /** * @brief Move assignment operator. * * @param other Input sptr object. * @note Original strong reference in target sptr object will be removed. */ sptr<T> &operator=(sptr<T> &&other); /** * @brief Copy Constructor for sptr with different managed class type(O). * * @tparam O Another specific class type managed by `other`. * @param other Input sptr object. */ template <typename O> sptr(const sptr<O> &other); /** * @brief Constructor only used in promote process of wptr. * * @param p WeakRefCounter object which hold the reference to the * managed object. * @param force Only used to identify from other constructor. */ inline sptr(WeakRefCounter *p, bool force); /** * @brief Get the pointer to the managed object. * * @return Pointer of the specific managed class type. */ inline T *GetRefPtr() const { return refs_; } /** * @brief Set the pointer to the managed object. * * @param other Another pointer object to be managed by sptr. * @note Avoid using independently, otherwise it will * cause mismatch of reference count and thus memory problems. */ inline void ForceSetRefPtr(T *other); /** * @brief Remove the reference to the managed object held by current sptr. * * @note It will make this sptr a "null sptr". */ void clear(); /** * @brief Type conversion operator. * * @return Raw pointer to the managed object. * @note Sptr object itself will not be converted, only the member raw * pointer returns. */ inline operator T *() const { return refs_; } /** * @brief Dereference operator. * * It will return the object managed by this sptr. * * @return Return reference of specific object managed by sptr. */ inline T &operator*() const { return *refs_; } /** * @brief Member selection operator. * * It will return the specified member of the object managed by this sptr. */ inline T *operator->() const { return refs_; } /** * @brief Logical-NOT operator. Check if sptr is a "null sptr". * * @return Return true if sptr is a "null sptr". */ inline bool operator!() const { return refs_ == nullptr; } /** * @brief Copy assignment operator with specified object to be managed. * * @note Original reference will be removed, then new reference to the * input object will be established. * @param other Another object to be managed by this sptr. */ sptr<T> &operator=(T *other); /** * @brief Copy assignment operator for sptr with * same managed class type(T). * * @note Original reference will be removed, this sptr will manage the * same object with the input sptr object. * @param other Another sptr object with same managed class type(T). */ sptr<T> &operator=(const sptr<T> &other); /** * @brief Copy assignment operator for wptr with * same managed class type(T). * * @note Original reference will be removed, this sptr will manage the * same object with the input wptr object. * @note This may fail, then this sptr will turn to be a "null sptr". * @param other Another wptr object with same managed class type(T). */ sptr<T> &operator=(const wptr<T> &other); /** * @brief Copy assignment operator for sptr with * different managed class type(O). * * @note Original reference will be removed, this sptr will manage the * same object with the input sptr object. * @note This sptr will interpret the managed object as a type T. * @param other Another sptr object with different managed class type(O). */ template <typename O> sptr<T> &operator=(const sptr<O> &other); /** * @brief Equal-to operator between sptr and a raw pointer. * * @param other Input raw pointer. * @return Return true if sptr point to the same object with input * raw pointer. */ bool operator==(const T *other) const; /** * @brief Not-equal-to operator between sptr and a raw pointer. * * @param other Input raw pointer. * @return Return true if sptr does not point to the same object with input * raw pointer. */ inline bool operator!=(const T *other) const { return !operator==(other); } /** * @brief Equal-to operator between sptr and a wptr. * * @param other Input wptr. * @return Return true if sptr and wptr are managing same object. */ bool operator==(const wptr<T> &other) const; /** * @brief Not-equal-to operator between sptr and a wptr. * * @param other Input wptr. * @return Return true if sptr and wptr are not managing same object. */ inline bool operator!=(const wptr<T> &other) const { return !operator==(other); } /** * @brief Equal-to operator between sptrs. * * @param other Input sptr. * @return Return true if two sptrs are managing same object. */ bool operator==(const sptr<T> &other) const; /** * @brief Not-equal-to operator between sptrs. * * @param other Input sptr. * @return Return true if two sptrs are not managing same object. */ inline bool operator!=(const sptr<T> &other) const { return !operator==(other); } private: T *refs_ = nullptr; // Raw pointer to the managed specific object }; template <typename T> inline void sptr<T>::ForceSetRefPtr(T *other) { refs_ = other; } template <typename T> inline sptr<T>::sptr() { refs_ = nullptr; } template <typename T> inline sptr<T>::sptr(T *other) { refs_ = other; if (refs_ != nullptr) { refs_->IncStrongRef(this); } } template <typename T> inline sptr<T>::sptr(const sptr<T> &other) { refs_ = other.GetRefPtr(); if (refs_ != nullptr) { refs_->IncStrongRef(this); } } template <typename T> sptr<T>::sptr(sptr<T> &&other) { refs_ = other.GetRefPtr(); other.ForceSetRefPtr(nullptr); } template <typename T> sptr<T> &sptr<T>::operator=(sptr<T> &&other) { if (refs_ != nullptr) { refs_->DecStrongRef(this); } refs_ = other.GetRefPtr(); other.ForceSetRefPtr(nullptr); return *this; } template <typename T> template <typename O> sptr<T>::sptr(const sptr<O> &other) : refs_(other.GetRefPtr()) { if (refs_ != nullptr) { refs_->IncStrongRef(this); } } template <typename T> inline sptr<T> &sptr<T>::operator=(T *other) { if (other != nullptr) { other->IncStrongRef(this); } if (refs_ != nullptr) { refs_->DecStrongRef(this); } refs_ = other; return *this; } template <typename T> inline sptr<T> &sptr<T>::operator=(const sptr<T> &other) { T *otherRef(other.GetRefPtr()); if (otherRef != nullptr) { otherRef->IncStrongRef(this); } if (refs_ != nullptr) { refs_->DecStrongRef(this); } refs_ = otherRef; return *this; } template <typename T> inline sptr<T> &sptr<T>::operator=(const wptr<T> &other) { if (refs_ != nullptr) { refs_->DecStrongRef(this); } if ((other != nullptr) && other.AttemptIncStrongRef(this)) { refs_ = other.GetRefPtr(); } else { refs_ = nullptr; } return *this; } template <typename T> template <typename O> sptr<T> &sptr<T>::operator=(const sptr<O> &other) { T *otherRef(other.GetRefPtr()); if (otherRef != nullptr) { otherRef->IncStrongRef(this); } if (refs_ != nullptr) { refs_->DecStrongRef(this); } refs_ = otherRef; return *this; } template <typename T> inline bool sptr<T>::operator==(const T *other) const { return other == refs_; } template <typename T> inline bool sptr<T>::operator==(const wptr<T> &other) const { return refs_ == other.GetRefPtr(); } template <typename T> inline bool sptr<T>::operator==(const sptr<T> &other) const { return refs_ == other.GetRefPtr(); } template<typename T> void sptr<T>::clear() { if (refs_) { refs_->DecStrongRef(this); refs_ = 0; } } template <typename T> inline sptr<T>::~sptr() { if (refs_ != nullptr) { refs_->DecStrongRef(this); } } template <typename T> inline sptr<T>::sptr(WeakRefCounter *p, bool /* force */) { if ((p != nullptr) && p->AttemptIncStrongRef(this)) { refs_ = reinterpret_cast<T *>(p->GetRefPtr()); } else { refs_ = nullptr; } }
最新发布
10-07
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值