重载操作符 - C++重载'->'符号是怎么实现的
PHPz
PHPz 2017-04-17 13:31:21
0
3
758

例如下面的代码:

class StrPtr{
public:
    StrPtr() : _ptr(nullptr){}
    //拷贝构造函数等省略...
    std::string* operator->()
    {
        return _ptr;
    }
private:
    std::string *_ptr;
};
std::string* operator->()
/*
这句代码的格式不是类似于前导运算符吗?类似于std::string operator*(){//...}不正是*ptr?但是重载的->运算符却是ptr->这样使用的,请问是为什么?
而且std::string* operator->()返回的是指针,为什么可以直接在后面访问类成员,[比如说ptr->size()]?
我的疑问是这是如何实现的,这和我对运算符重载的直接理解有所差异。
*/
PHPz
PHPz

学习是最好的投资!

reply all(3)
洪涛

Isn’t the format of this code similar to the leading operator? Similar to std::string operator*(){//...}, isn’t it exactly *ptr? But the overloaded -> operator is used like this. Why? ptr->

Because the associativity of operators is different, dereference operator (

) is right associative, while member access operator (* and .) is left associative. This is an example of different associativity of different symbols. In C++, when the symbols are the same, there will also be examples of different usages resulting in different associativity. For example: ->

 operator | left associative            | right associative
          |   lhs op rhs  /  lhs op     |   op rhs
----------+-----------------------------+---------------------------
 ++ --    | postfix increment/decrement | prefix increment/decrement
 + -      | binary add/subtract         | unary plus/minus
 *        | binary multiply             | dereference
 &        | bitwise and                 | address-of
 ()       | function call               | type conversion
When you overload a left-associative operator (such as

, etc.), the operator is often a binary operator. For + - * / (), lhs op rhs or lhs.operator op(rhs) will be called. We Just overload the operator according to this function signature. operator op(lhs, rhs)

But what if this operator is a unary operator? This will be discussed on a case-by-case basis:

  • If this symbol has more than one usage (such as

    or ++), for -- (left combination) and lhs op (right combination), we have to find a way to distinguish different usages, So there will be function parameters for placeholders (op rhs): int

    • for

      call lhs oplhs.operator op(int)

    • for

      call op rhsrhs.operator op()

  • If this symbol has only one usage (such as

    ), for ->, there is no need for placeholder function parameters, and it can be directly written as a right-associative function signature, that is, lhs op

    • for

      call lhs oplhs.operator op()

So it makes people a little confused why the function signatures are similar but the usage is different.

and

returns a pointer, why can we directly access class members later, [for example, std::string* operator->()]? ptr->size()

This is specified by the C++ standard. For

, the operator ptr->mem is interpreted differently depending on the type of ptr: ->

  • When the type of

    is a built-in pointer type, it is equivalent to ptr(*ptr).mem

  • When the type of

    is a class, it is equivalent to ptrptr.operator->()->mem

You will find that this is a recursive explanation. For

it will recurse into: ptr->mem

(*(ptr.operator->().operator->().….operator->())).mem
Operator

is unary, while operator -> is binary. Operator . ultimately accesses members through operator ->, and . operator is not allowed to be overloaded and can only be implemented by the compiler. .

For example, use the class

defined by the subject: StrPtr

string s = "abc";
StrPtr ptr(&s);
string *sp = &s;

ptr->size();
// 等价于 ptr.operator->()->size();
// 等价于 _ptr->size();   这跟 sp->size();   不就一样了吗
// 等价于 (*_ptr).size(); 这跟 (*sp).size(); 不就一样了吗

The last digression, C++ variable names should not start with an underscore. Starting with an underscore is reserved for the compiler. You can use m_somemember or somemember_ as a private member name.

Peter_Zhu

The standard stipulates that the way is written has nothing to do with the type of return value being a pointer.
And the magic is that if what is returned is not a pointer but a class object that overloads the arrow operator, then the arrow operator of the class object will be called until an arrow operator returns a raw Pointer, then retrieve the content of the pointer and perform member access. This process can be infinitely nested and recursed layer by layer. If the compiler searches layer by layer and does not find an arrow operator that returns a raw pointer, it will report an error. For example, if it returns a class object that does not have an overloaded arrow operator, or a value such as int.
If you follow the general operator overloading logic, then the arrow operator should return a reference, and there seems to be no problem logically. But if the above situation occurs, I'm afraid there will be something like
-> -> ... ->
. I think this is probably one of the reasons.
I hope to communicate with people who have different understandings.

刘奇

The standard stipulates that ->either calls -> that returns an object, returns a pointer, or reports an error.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template