C++探索之旅首页投稿(暂停使用,暂停投稿)C++

[cpp deep dive] `operator` deep

2016-08-11  本文已影响46人  Quasars

我在还没深入学习操作符重载的时候,虽然可以自己边debug边整出一个可以运行的操作符重载,但对其的了解却十分浅显,甚至连有些基本概念都搞不清楚.这大概就是传说中的半桶水.
我通过这篇文章已经解决了如下问题.

operator overloading函数在什么情况下会被调用?operator的操作数如何对应到函数参数?左操作数是用户自定义类?连续的operator如何处理?返回引用?编译器到底替我们生成了什么?

参考资料:

操作符重载分为三种:

It turns out that there are three different ways to overload operators:

关于这三者,c++ ref上面有个表:

Paste_Image.png

需要注意的点:

Phase_1 the friend function way(9.2 quiz)

交作业:

  1 #include <iostream>                                                                                                                                                                                                                                              
  2 class Fraction{                                                                 
  3     private:                                                                    
  4         int nmrt_;                                                              
  5         int dnmnt_;                                                             
  6     public:                                                                     
  7        /*                                                                       
  8         Fraction(int a, int b) : nmrt_(a), dnmnt_(b){                           
  9             if(!(nmrt_ % dnmnt_)){                                              
 10                 nmrt_ = nmrt_ / dnmnt_;                                         
 11                 dnmnt_ = 1;                                                     
 12             }else if(!(dnmnt_ % nmrt_)){                                        
 13                 dnmnt_ = dnmnt_ / nmrt_;                                        
 14                 nmrt_ = 1;                                                      
 15             }                                                                   
 16         }                                                                       
 17         */                                                                      
 18         Fraction(int a, int b) : nmrt_(a / gcd(a,b)), dnmnt_(b / gcd(a, b)){ }  
 19         void print();                                                           
 20         friend Fraction operator*(int a,const Fraction &m);                     
 21         friend Fraction operator*(const Fraction &m, int a);                    
 22         friend Fraction operator*(const Fraction &lhs, const Fraction &rhs);    
 23         static int gcd(int a, int b);                                           
 24 };                                                                              
 25                                                                                 
 26 inline void Fraction::print(){                                                  
 27     std::cout << nmrt_ << "/" << dnmnt_ <<std::endl;                            
 28 }                                                                               
 29 inline Fraction operator*(int a,const Fraction &m){                             
 30     return Fraction(a*m.nmrt_, m.dnmnt_);                                       
 31 }                                                                               
 32 inline Fraction operator*(const Fraction &m, int a){                            
 33     return a*m;                                                                 
 34 }                                                                               
 35 inline Fraction operator*(const Fraction &lhs,const Fraction &rhs){             
 36     int a = lhs.nmrt_ * rhs.nmrt_;                                              
 37     int b = lhs.dnmnt_ * rhs.dnmnt_;                                            
 38         return Fraction(a, b);                                                  
 39 }                                                                               
 40 inline void swap(int &a, int &b)                                                
 41 {                                                                               
 42     int tmp = a;                                                                
 43     a = b;                                                                      
 44     b = tmp;                                                                    
 45 }                                                                               
 46                                                                                 
 47 int Fraction::gcd(int a, int b){                                                
 48     int c;                                                                      
 49 //    if(a < b){  swap is not needed                                            
 50 //        swap(a, b);                                                           
 51 //    }                                                                         
 52     while((c = a % b) != 0){//gcd(a,b) == gcd(b, a%b) 辗转相除法                 
 53         a = b;
 54         b = c;                                                                  
 55     }                                                                           
 56     return b;                                                                   
 57 }                                                                               
 58                                                                                 
 59                                                                                 
 60                                                                                 
 61 int main(){                                                                     
 62     Fraction f1(2, 5);                                                          
 63     f1.print();                                                                 
 64                                                                                 
 65     Fraction f2(3, 8);                                                          
 66     f2.print();                                                                 
 67                                                                                 
 68     Fraction f3 = f1 * f2;                                                      
 69     f3.print();                                                                 
 70                                                                                 
 71     Fraction f4 = f1 * 2;                                                       
 72     f4.print();                                                                 
 73                                                                                 
 74     Fraction f5 = 2 * f2;                                                       
 75     f5.print();                                                                 
 76                                                                                 
 77     Fraction f6 = Fraction(1, 2) * Fraction(2, 3) * Fraction(3, 4);             
 78     f6.print();                                                                 
 79                                                                                 
 80     return 0;                                                                   
 81 }

Phase_2 关于使用引用参数/引用返回值


Phase 3_上代码 关于返回引用

  1 #include <cstdio>
  2 #include <string>
  3 #include <iostream>
  4 #include <assert.h>
  5 #define MAX 128
  6 class mystr{
  7     private:
  8         std::string data_;
  9     public:
 10         mystr() : data_(""){ }// once you define a constructor, compiler won't generate this default one.
 11         mystr(const std::string & str) : data_(str){ }//const-ref as para is ok & good practice.
 12 explicit mystr(const char* cstr) : data_(cstr){ }
 13         mystr(int x){
 14             char buf[MAX];
 15             snprintf(buf, MAX, "%d", x);
 16             data_ = std::string(buf);
 17         }
 18         mystr(const mystr& m) : data_(m.data_){}//this's like a default copy constructor
 19
 20         mystr& operator=(const mystr& m){// once you define a constructor, compiler won't generate this default     one.
 21             if(&m == this){
 22                 return *this;
 23             }
 24             data_ = m.data_;
 25             return *this;
 26         }
 27
 28         mystr& operator=(const char * cstr){
 29             data_ = cstr;
 30             return *this;
 31         }
 32
 33         void print(){
 34             printf("%s\n", data_.c_str());
 35         }
 36         mystr operator+(const mystr &x)//member operator overloading
 37         {
 38             printf("member func is called.\n");
 39             return mystr(this->data_ + x.data_);
 40         }
 41         char& operator[](int x)
 42         {
 43          //@return's type must be a ref. for modifying it.
 44          //if return as value, it is a temp-object which is not allowed to modify.
 45             assert(x < data_.size());
 46             return data_[x];
 47         }
 48
 49
 50         friend std::ostream & operator<<(std::ostream& out, const mystr &y);
 51         //@out is non-const because we will modified it.so as the @return.
 52         //@out & @return must be ref because stream object can't be copyed or assigned.
 53
 54         friend mystr operator+(const mystr &x, const mystr &y);//const is needed for auto-type-convert
 55 };
 56 //version 1
 57 mystr operator+(const mystr &x, const mystr &y)
 58 //here must be a const-ref para, or it won't auto-convert from const char*/int/std::string into mystr.
 59 {
 60     std::string s = x.data_;
 61     s.append(y.data_);
 62     return mystr(s);
 63 }
 64
 65 std::ostream& operator<<(std::ostream &out, const mystr &y)
 66 {
 67     return out << y.data_;
 68 }
 69
 70 //version 2
 71 //mystr operator+(mystr &x, mystr &y)
 72 //{
 73 //    return mystr(x.data_ + y.data_);
 74 //}
 75
 76 int test1()//const-ref as para is ok & good practice.
 77 {
 78     std::string * ss = new std::string("12345");
 79     mystr a(*ss);
 80     delete ss;
 81     a.print();
 82     return 0;
 83 }
 84
 85 int test2()//
 86 {
 87     mystr ss = mystr("123") + mystr("abc");
 88     ss.print();
 89     return 0;
 90 }
 91
 92 int test3()
 93 {
 94     std::cout << mystr("1234") << mystr("2345") << std::endl;
 95     return 0;
 96 }
 97
 98 int test4()//test for operator[] & modify it.
 99 {
100     mystr ss("01234");
101
102     std::cout << ss[0] << std::endl;
103
104     ss[0] = 'A';
105
106     std::cout << ss << std::endl;
107
108     return 0;
109 }
110
111 int test5()
112 {
113     mystr ss("1234");
114     mystr s2;
115     s2 = ss;
116     std::cout << s2 << std::endl;
117     return 0;
118 }
119
120 int main(){
121     return test5();
122 }

下面摘录了一些资料

The Three Basic Rules of Operator Overloading in C++
When it comes to operator overloading in C++, there are three basic rules you should follow. As with all such rules, there are indeed exceptions. Sometimes people have deviated from them and the outcome was not bad code, but such positive deviations are few and far between. At the very least, 99 out of 100 such deviations I have seen were unjustified. However, it might just as well have been 999 out of 1000. So you’d better stick to the following rules.
Whenever the meaning of an operator is not obviously clear and undisputed, it should not be overloaded. Instead, provide a function with a well-chosen name.Basically, the first and foremost rule for overloading operators, at its very heart, says: Don’t do it. That might seem strange, because there is a lot to be known about operator overloading and so a lot of articles, book chapters, and other texts deal with all this. But despite this seemingly obvious evidence, there are only a surprisingly few cases where operator overloading is appropriate. The reason is that actually it is hard to understand the semantics behind the application of an operator unless the use of the operator in the application domain is well known and undisputed. Contrary to popular belief, this is hardly ever the case.

Always stick to the operator’s well-known semantics.
C++ poses no limitations on the semantics of overloaded operators. Your compiler will happily accept code that implements the binary + operator to subtract from its right operand. However, the users of such an operator would never suspect the expression a + b to subtract a from b. Of course, this supposes that the semantics of the operator in the application domain is undisputed.

Always provide all out of a set of related operations.
Operators are related to each other* and to other operations.
If your type supportsa + b, users will expect to be able to call a += b
, too.
If it supports prefix increment ++a, they will expect a++ to work as well.
If they can check whether a < b, they will most certainly expect to also to be able to check whether a > b.
If they can copy-construct your type, they expect assignment to work as well.


C++98/03 what the compiler generate for us ?##

If they are needed,
the compiler will generate a default constructor for you unless you declare any constructor of your own.
the compiler will generate a copy constructor for you unless you declare your own.
the compiler will generate a copy assignment operator for you unless you declare your own.
the compiler will generate a destructor for you unless you declare your own.


When to use a normal, friend, or member function overload

In most cases, the language leaves it up to you to determine whether you want to use the normal/friend or member function version of the overload. However, one of the two is usually a better choice than the other.


The following rules of thumb can help you determine which form is best for a given situation:

上一篇 下一篇

猜你喜欢

热点阅读