[C + +] Some suggestions for overloaded operators

Source: Internet
Author: User
Tags types of functions

Operator 1. Carefully define type conversion functions

There are two types of functions that allow the compiler to make these conversions: a single-argument constructor (Single-argument constructors) and an implicit type conversion operator. A single-argument constructor is a constructor that can be called with only one argument. The function can either define only one parameter, or it can be a default value for all parameters after the first argument, although multiple parameters are defined.

A first example:

class Name {public:  Name(const string& s);... }; class Rational {public:  0,           1);...for names of things// 转换 string 到 // Name // 有理数类 // 转换 int 到 // 有理数类 };

The second example: the implicit type conversion operator is just a strange-looking member function: operator keyword followed by a class
Type symbol.

public: ...   operatordoubleconst;};// 在下面这种情况下,这个函数会被自动调用: Rational r(1, 2); double0.5 * r;// 转换 Rational 类成 // double 类型 // r 的值是 1/2 // 转换 r 到double, // 然后做乘法 

Possible problems with implicit type conversions:

 #include <iostream>  using  namespace  STD ; Span class= "Hljs-keyword" >class Rational {public : Rational (double  A, double  b) {val = a/b; } operator  double  () {return  Val; }private : double  val;}; int  Main () {Rational test (3 , 4 ); cout  << test << Endl; return  0 ;} 

We thought we didn't define operator <<, so the compiler would give an error, but actually the compiler would convert the test implicit type to a double type. This looks pretty good and actually comes back with a lot of unpredictable problems. It shows the disadvantage of implicit type conversions: = = Their presence will cause errors to occur = =.

The workaround is to replace the conversion operator with an equivalent function that does not use the syntax keyword. For example, to convert a Rational object to a double, use the Asdouble function instead of the operator double function:?

...   double asDouble() const;};// 这个成员函数能被显式调用: Rational r(12

In most cases, the use of this explicit conversion function is inconvenient, but the fact that the function is silently invoked will no longer occur, and this loss is worthwhile. As if writing a library, string does not give an implicit type to the char* operator, but gives the C_STR () to convert it.

The following discusses the issue of implicit type conversion for a single-parameter constructor.

T>?class Array {?public:   Array(int lowBound, int highBound);  Array(int size);  T& operator[](int index);  ...};

The first constructor allows the caller to determine the range of the array index, for example, from 10 to 20. It is a two-parameter constructor, so it cannot be a type conversion function. The second constructor allows the caller to simply define the number of elements in the array (using the same method as the built-in array), but the difference is that it can be used as a type conversion function, which can cause infinite pain.

For example, compare the array object with some of the code below:

BOOLoperator== (Constarray<int>& LHS, array<int> A (Ten);Constarray<int>& RHS); array<int> B (Ten);... for(inti =0; I <Ten; ++i)if(A = = B[i]) { DoSomething forWhen A[i] and B[i] isEqual;}Else{//Hey Yo! "A" should be "a[i ."     DoSomething forWhen they ' re not; }

We want to compare each element of a with each element of B, but when we enter a, we accidentally forget the array subscript. Of course we want the compiler to report a variety of warning messages, but it doesn't. Because it sees this invocation as calling the operator== function with the array parameter (for a) and int (for the b[i]) parameter, but no operator== function is such a parameter type, our compiler notices that it can convert int type to by calling the array constructor Array type, this constructor has only one parameter of type int. The compiler then compiles so that the generated code is like this:

for (int010; ++i)  ifstatic_cast< Array<int> >(b[i]))   ...

Each cycle compares the content of a with a temporary array of size B[i] (the content is undefined). This is not only impossible to run in the right way, but also inefficient. Because each cycle we have to create and release an array object.

The solution is to take advantage of the features of an up-to-date compiler, explicit keywords. This feature, which is specifically introduced to address implicit type conversions, is well understood in the way it is used. The constructor is declared with explicit, and if this is done, the compiler rejects calling the constructor for implicit type conversions. An explicit type conversion is still valid:

Template<classT> class Array {public:...? explicit Array (int size); Note Use"Explicit" ...}; Array<int> A (Ten); Array<int> B (Ten);if(A = = B[i])...if(A = = Array<int> (B[i]))...Correct, explicit constructor//can be used properly when building objects//also correct?//Error! There is no way//implicit conversion?//int to array<int>//correct, explicit from int to//array<int> conversion//(But code logic//unreasonable)if(A = = static_cast< array<int> > (b[i]))...Equally correct, same//unreasonable?if(A = = (array<int>) b[i])...C-style conversion is also correct,//But logic//is still unreasonable

About explicit: (parameter implicit type conversion is not allowed!) )

Class test1{ Public:Test1(intn) {num=n; }//General ConstructorsPrivate:intNum;}; Class test2{ Public:ExplicitTest2(intn) {num=n; }//explicit (Explicit) constructorsPrivate:intNum;};intMain () {Test1 t1= A;//implicitly calls its constructor, successfullyTest2 t2= A;//Compile error, its constructor cannot be called implicitlyTest2 T2 ( A);//Explicit invocation succeededreturn0;}
2. Self-increment and self-reduction

The difference between overloaded functions depends on the difference in their parameter types, but there is only one argument for either increment or decrement prefixes or suffixes. To solve this problem, C + + specifies that the suffix form has an int type parameter, and when the function is called, the compiler passes a value of 0 as the int parameter to the function:?

class UPInt {?public:   operator++();  constoperator++(int);  operator--();  constoperator--(int);  operator+=(int);... }; UPInt i;++i;i++;--i;i--;

It is worth noting that the = = prefix returns a reference, and the suffix returns a const object = =. (It is easy to interpret rationality by the difference between prefix increment and suffix increment.) )

UPInt& UPInt::operator++() {  *this1;  return *this;}const UPInt UPInt::operator++(int) {  UPInt oldValue = *this;++(*this// 增加 return oldValue;

If the suffix increment is not a const object, then the following code is correct:

// 两次 increment 后缀 这组代码与下面的代码相同: i.operator++(0).operator++(0);
3. Do not overload &&, | | or ","

C + + uses the = = Boolean expression short-circuit evaluation method = = (short-circuit evaluation). This means that once the true and false values of the Boolean expression are determined, the Boolean expression stops the operation even if some of the expressions are not tested.

char *p;?...?if010...0 的测试失败,strlen 不会被调用。同样:?int rangeCheck(int index)?{?if...?...?}

C + + allows customization of && and | | Based on user-defined types Operator. Methods are overloaded functions operator&& and operator| |, you can overload in global overloads or in each class. But you lose the characteristic of the short-circuit evaluation.

if...if...                              // when operator&& is a                              functionif...                              function

This seems to be no different, but the function call method and the short-circuit evaluation method are absolutely different. First, when the function is called, all its arguments need to be calculated, so the calling function functions operator&& and operator| | , two parameters need to be calculated, in other words, no short-circuit calculation method is used. The second is that the C + + language specification does not define the order in which the function parameters are evaluated, so there is no way to know which of the expressions 1 and 2 are evaluated first. It is entirely possible to reverse the short-circuit calculation with the order of the left parameter to the right parameter calculation.

Parts that cannot be overloaded:

. .* :: ?:?newdeletesizeoftypeidstatic_castdynamic_castconst_castreinterpret_cast

The parts that can be overloaded:

operatornewoperatordelete?operatornewoperatordelete

The purpose of operator overloading is to make the program easier to read, write, and understand than to confuse others with your knowledge. If you don't have a good reason to overload the operator, do not overload it. in encounters with &&, | |, and, when, finding a good reason is difficult, because no matter how hard you try, you can't let them behave as expected.

4. Understand the different meanings of new and delete

String *ps = new String ("Memory Management");?
The new you are using is the new operator. This operator, like sizeof, is a language built-in, and you can't change its meaning, it always functions the same. The function it is to complete is divided into two parts. The first part is to allocate enough memory to accommodate the object of the desired type. The second part is that it calls the constructor to initialize the in-memory object. The new operator always does both things, and you can't change its behavior in any way.

New operator

What you can change is how to allocate memory for an object. The new operator calls a function to complete the required memory allocation, and you can override or reload the function to change its behavior. The name of the function called by the new operator for allocating memory is operator new.
The function operator new usually declares this:

void *rawmemory = operator new (sizeof (string));
The operator operator new returns a pointer to a piece of memory sufficient to hold a string object.
Just like malloc, operator New's responsibility is simply to allocate memory. It has no knowledge of the constructor function. Operator new is aware of memory allocations. It is the work of the new operator to pass an unhandled pointer returned by operator new to an object.

Placement NEW

But sometimes you have some (raw) memory that has already been allocated but not processed, and you need to construct an object in these memory. You can use a special operator new, which is called placement new.

voidoperatornewvoid *location)?{   return location;}

The purpose of operator new is to allocate memory for an object and then return a pointer to that memory. In the case of placement new, the caller has obtained a pointer to memory because the caller knows where the object should be placed. What placement new has to do is return the pointer to it. (The unused (but mandatory) parameter size_t has no name to prevent the compiler from issuing a warning that it is not being used.) )

Delete Memory deallocation

Operator Delete is used to free memory, which is declared like this:

voidoperatordelete(void

So

Causes the compiler to generate code similar to this:?

ps->~string(); // call the object‘s dtor operator delete(ps); // deallocate the memory // the object occupied 

The implication of this is that if you only want to handle uninitialized memory, you should bypass the new and delete
operator while calling operator new to get memory and operator delete to release memory to the system:

void *buffer =  operatornew(50*sizeof(char));  // 分配足够的?// 内存以容纳 50 个 char ...operatordelete(buffer);//没有调用构造函数 // 释放内存 // 没有调用析构函数 

If you are building an object in memory with placement new, you should avoid using the delete operator in that memory.
Because the delete operator calls operator delete to free memory, but the memory that contains the object is not originally allocated by operator new, placement new simply returns the pointer that is forwarded to it. Who knows where this pointer comes from? Instead, you should explicitly call the object's destructor to remove the effect of the constructor:?

//functions for allocating and freeing memory in shared memoryvoid *mallocshared (size_t size);?voidFreeshared (void *memory);?void *Sharedmemory=mallocshared (sizeof (Widget));? Widgets*pw= //As shown above,Constructwidgetinbuffer (Sharedmemory,Ten);//Use...Delete PW;PW -~widget (); freeshared (PW);//The results are not sure! Shared memory from?//mallocshared, not operator new//correct. Deconstruct the Widget that the PW points to,?//But not released?//memory that contains widgets?//correct. Release the shared memory that PW points to?//But no destructor is called

The new and delete operators are built-in, and their behavior is not controlled by you, and the memory allocation and deallocation functions they call can be controlled. When you want to customize the behavior of the new and delete operators, keep in mind that you can't really do that. You can only change the way they are used to accomplish their functions, and the functions they perform are fixed by the language and cannot be changed. (Can modify how they does what they does, but what they does is fixed by the language)

[C + +] Some suggestions for overloaded operators

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.