1. 程式人生 > 實用技巧 >C++ inline與operator

C++ inline與operator


title: C++ inline與operator
date: 2020-03-10
categories: c++
tags: [c++]

inline修飾符,operator關鍵字

1.inline修飾符-行內函數

1.1為何需要inline

在 c/c++ 中,為了解決一些頻繁呼叫的小函式大量消耗棧空間(棧記憶體)的問題,特別的引入了 inline 修飾符,表示為行內函數。
在系統下,棧空間是有限的,大量呼叫函式會造成棧溢位

inline char* dbtest(int a) {
    return (i % 2 > 0) ? "奇" : "偶";
} 
 
int main()
{
   int i = 0;
   for (i=1; i < 100; i++) {
       printf("i:%d    奇偶性:%s /n", i, dbtest(i));    
   }
}

每個 for 迴圈的內部任何呼叫 dbtest(i) 的地方都換成了 (i%2>0)?"奇":"偶",這樣就避免了頻繁呼叫函式對棧記憶體重複開闢所帶來的消耗。

1.2 inline使用限制

inline 的使用是有所限制的,inline 只適合涵數體內程式碼簡單的涵數使用,不能包含複雜的結構控制語句例如 while、switch,並且不能行內函數本身不能是直接遞迴函式(即,自己內部還呼叫自己的函式)。

1.3 inline僅是一個對編譯器的建議

inline 函式僅僅是一個對編譯器的建議,所以最後能否真正內聯,看編譯器的意思,它如果認為函式不復雜,能在呼叫點展開,就會真正內聯

1.4 建議 inline 函式的定義放在標頭檔案中

因為行內函數要在呼叫點展開,所以編譯器必須隨處可見行內函數的定義,要不然就成了非行內函數的呼叫了。所以,這要求每個呼叫了行內函數的檔案都出現了該行內函數的定義。因此,將行內函數的定義放在標頭檔案裡實現是合適的,省卻你為每個檔案實現一次的麻煩。

1.5 類中的成員函式與inline

定義在類中的成員函式預設都是內聯的,如果在類定義時就在類內給出函式定義,那當然最好。如果在類中未給出成員函式定義,而又想內聯該函式的話,那在類外要加上 inline,否則就認為不是內聯的。

class A
{
    public:void Foo(int x, int y) {  } // 自動地成為行內函數
}
// 標頭檔案
class A
{
    public:
    void Foo(int x, int y);
}
 
// 定義檔案
inline void A::Foo(int x, int y){}   //注意 A::

1.6 inline 是一種"用於實現的關鍵字"

關鍵字 inline 必須與函式定義體放在一起才能使函式成為內聯,僅將 inline 放在函式宣告前面不起任何作用。

如下風格的函式 Foo 不能成為行內函數:

inline void Foo(int x, int y); // inline 僅與函式宣告放在一起
void Foo(int x, int y){}
而如下風格的函式 Foo 則成為行內函數:

void Foo(int x, int y);
inline void Foo(int x, int y) {} // inline 與函式定義體放在一起

1.7 慎用 inline

內聯是以程式碼膨脹(複製)為代價,僅僅省去了函式呼叫的開銷,從而提高函式的執行效率。
如果執行函式體內程式碼的時間,相比於函式呼叫的開銷較大,那麼效率的收穫會很少。另一方面,每一處行內函數的呼叫都要複製程式碼,將使程式的總程式碼量增大,消耗更多的記憶體空間。
以下情況不宜使用內聯:
(1)如果函式體內的程式碼比較長,使用內聯將導致記憶體消耗代價較高。
(2)如果函式體內出現迴圈,那麼執行函式體內程式碼的時間要比函式呼叫的開銷大。類的建構函式和解構函式容易讓人誤解成使用內聯更有效。要當心建構函式和解構函式可能會隱藏一些行為,如"偷偷地"執行了基類或成員物件的建構函式和解構函式。所以不要隨便地將建構函式和解構函式的定義體放在類宣告中。一個好的編譯器將會根據函式的定義體,自動地取消不值得的內聯(這進一步說明了 inline 不應該出現在函式的宣告中)。

2 operator

參考:https://blog.csdn.net/liitdar/article/details/80654324

2.1 operator簡介

operator 是C++的一個關鍵字,它和運算子(如=)一起使用,表示一個運算子過載函式,在理解時可將operator和運算子(如operator=)視為一個函式名。

使用operator過載運算子,是C++擴充套件運算子功能的方法。使用operator擴充套件運算子功能的原因如下:

使過載後的運算子的使用方法與過載前一致。
擴充套件運算子的功能只能通過函式的方式實現(實際上,C++中各種“功能”都是由函式實現的)。

2.2 使用operator的原因

對於C++提供的所有操作符,通常只支援對於基本資料型別和標準庫中提供的類的操作,而對於使用者自己定義的類,如果想要通過該操作符實現一些基本操作(比如比較大小,判斷是否相等),就需要使用者自己來定義關於這個操作符的具體實現了。

比如,我們要設計一個名為“person”的類,現在要判斷person類的兩個物件p1和p2是否一樣大,我們設計的比較規則是按照其年齡來比較,那麼,在設計person類的時候,就可以通過對操作符“”進行過載,來使用操作符“”對物件p1和p2進行比較了(根據前面的分析,實際上比較的內容應該是person類中的資料成員“age”)。

我們上面說的對操作符“”進行過載,說是“過載”,是由於編譯器在實現操作符“”功能的時候,已經為我們提供了這個操作符對於一些基本資料型別的操作支援,只不過由於現在該操作符所操作的內容變成了我們自定義的資料型別(如class),而預設情況下,該操作符是不能對我們自定義的class型別進行操作的,所以,就需要我們通過過載該操作符,給出該操作符操作我們自定義的class型別的方法,從而達到使用該操作符對我們自定義的class型別進行運算的目的。

2.3 how

實現一個操作符過載的方式通常分為兩種情況:

將操作符過載實現為類的成員函式;
操作符過載實現為非類的成員函式(即全域性函式)。

2.3.1 將操作符過載實現為類的成員函式

在類體中宣告(定義)需要過載的操作符,宣告方式跟普通的成員函式一樣,只不過操作符過載函式的名字是“關鍵字 operator +一個C++預定義的操作符”

    bool operator==(const person& ps)const
    {
        if (this->age == ps.age)
        {
            return true;
        }
        return false;
    }

class person
{
private:
    int age;
public:
    person(int nAge)
    {
        this->age = nAge;
    }
 
    bool operator==(const person& ps)const
    {
        if (this->age == ps.age)
        {
            return true;
        }
        return false;
    }
};

通過inline和const的學習,建議改成

class person{
private:
    int age;
    public:
    person(int a){
       this->age=a;
    }
   inline bool operator == (const person &ps) const;
};

// 實現
inline bool person::operator==(const person &ps) const
{
  if (this->age==ps.age)
     return true;
  return false;
}


    person p1(10);
    person p2(10);
    
    if (p1 == p2)

2.3.2 操作符過載實現為非類的成員函式(即全域性函式)

對於全域性過載操作符,代表左運算元的引數必須被顯式指定。

class person
{
public:
    int age;
};
 
// 左運算元的型別必須被顯式指定
// 此處指定的型別為person類
bool operator==(person const& p1 ,person const& p2)
{
    if (p1.age == p2.age)
    {
        return true;
    }
    else
    {
        return false;
    }
}
 
int main()
{
    person p1;
    person p2;
    p1.age = 18;
    p2.age = 18;
 
    if (p1 == p2)
    {
        cout << "p1 is equal with p2." << endl;
    }
    else
    {
        cout << "p1 is NOT equal with p2." << endl;
    }
 
    return 0;
}

2.3.4 操作符過載的方式選擇

如果一個過載操作符是類成員,那麼只有當與它一起使用的左運算元是該類的物件時,該操作符才會被呼叫;而如果該操作符的左運算元確定為其他的型別,則操作符必須被過載為全域性函式;

C++要求'='、'[]'、'()'、'->'操作符必須被定義為類的成員操作符,把這些操作符通過全域性函式進行過載時會出現編譯錯誤

如果有一個運算元是類型別(如string類),那麼對於對稱操作符(比如==操作符),最好通過全域性函式的方式進行過載。

2.3.5 操作符過載的限制

實現操作符過載時,需要注意:

過載後操作符的運算元至少有一個是使用者定義型別;
不能違反原來運算元的語法規則;
不能建立新的操作符;
不能過載的操作符包括(以空格分隔):sizeof . .* :: ?: RTTI型別運算子
=、()、[]、以及 ->操作符只能被類的成員函式過載

(1) 只有C++預定義的操作符集中的操作符才可以被過載;

(2)對於內建型別的操作符,它的預定義不能被改變,應不能為內建型別過載操作符,如,不能改變int型的操作符+的含義;

(3) 也不能為內建的資料型別定義其它的操作符;

(4) 只能過載類型別或列舉型別的操作符;

(5) 過載操作符不能改變它們的操作符優先順序;

(6) 過載操作符不能改變運算元的個數;

(7) 除了對( )操作符外,對其他過載操作符提供預設實參都是非法的;

最後https://www.cnblogs.com/ZY-Dream/p/10068993.html 這裡更詳細

https://blog.csdn.net/liitdar/article/details/80656156 operator=