2009-03-02 91 views
5

背景重載operator << - C++

我有一個使用矢量<的std :: string >內部容器類。我已經提供了一個方法AddChar(std :: string)到這個包裝類,它向內部向量執行push_back()。在我的代碼中,我必須在容器中添加多個項目。爲此我必須使用

container.AddChar("First"); 
container.AddChar("Second"); 

這會使代碼變大。所以爲了使它更容易,我計劃超載運營商< <。所以我可以寫

container << "First" << "Second" 

和兩個項目將被添加到底層向量。

這裏是我用於該

class ExtendedVector 
{ 
private: 
    vector<string> container; 

public: 
    friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){ 
     cont.AddChar(str); 
     return cont; 
    } 

    void AddChar(const std::string str) 
    { 
     container.push_back(str); 
    } 

    string ToString() 
    { 
     string output; 
     vector<string>::iterator it = container.begin(); 
     while(it != container.end()) 
     { 
      output += *it; 
      ++it; 
     } 
     return output; 
    } 
}; 

它按預期工作的代碼。

問題

  1. 是運算符重載正確寫入?
  2. 在這種情況下重載操作符是否是一種好習慣?
  3. 此代碼是否會有任何性能問題或任何其他問題?

有什麼想法?

編輯

聽到很好的意見後,我決定不超載< <,因爲它沒有意義在這裏。我刪除了運算符重載代碼,這裏是最終的代碼。

class ExtendedVector 
{ 
private: 
    vector<string> container; 

public: 

    ExtendedVector& AddChar(const std::string str) 
    { 
     container.push_back(str); 
     return *this; 
    } 

     .. other methods 
} 

這讓我通過使用params關鍵字添加

container.AddChar("First").AddChar("Second") 

在C#中,我可以做到這一點更容易。代碼會像

void AddChar(params string[] str) 
{ 
    foreach(string s in str) 
     // add to the underlying collection 
} 

我知道在C++中,我們可以使用...指定的參數變量langth。但是AFAIK,它不是類型安全的。那麼這是一個推薦的做法嗎?所以我可以寫

container.AddChar("First","Second") 

感謝您的答覆。

+0

Qt使用operator <<爲QStringList:http://doc.trolltech.com/4.4/qstringlist.html,我喜歡它的使用。但通常我會注意不要添加太多的操作員。正如凱文所說,它可能會變得混亂如地獄:) – 2009-03-02 04:09:38

回答

8

運算符過載是否正確寫入?

這是,但一個可以做得更好。像其他人提到的一樣,您的功能可以完全由現有的公共功能來定義。爲什麼不讓它只使用那些?現在,它是一個朋友,這意味着它屬於實現細節。如果您將運營商< <作爲會員加入班級,情況也是如此。但是,讓您的運營商< < a 非會員,非好友功能。

class ExtendedVector { 
    ... 
}; 

// note, now it is *entirely decoupled* from any private members! 
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){ 
    cont.AddChar(str); 
    return cont; 
} 

如果你改變你的類,你不會相信你的運營商< <仍然有效。但是,如果您的運營商僅完全依賴公共功能,則只有在對您的類的實施細節進行了更改後,才能確定它將起作用。好極了!

在這種情況下重載操作符是否是一種很好的做法?

另一個人再次說,這是有爭議的。在很多情況下,操作員超載會一眼看上去「整潔」,但明年看起來會像地獄一樣,因爲當你給某些符號特別的愛時,你不會再想到你心中的想法。在運營商< <的情況下,我認爲這是確定的用途。它用作流的插入運算符是衆所周知的。而且我知道的是廣泛使用的情況下,像

QStringList items; 
items << "item1" << "item2"; 

一個類似的例子是boost.format這也重用operator%傳遞參數的佔位符的字符串Qt和KDE應用程序:

format("hello %1%, i'm %2% y'old") % "benny" % 21 

這是當然的也有爭議在那裏使用它。但其用於printf格式的指定是衆所周知的,所以它的使用也是可以的,imho。但一如既往,風格也是主觀的,所以帶上一粒鹽:)

我怎樣才能接受類型安全方式的變長參數?

嗯,是接受,如果你正在尋找均質參數向量的方法:

void AddChars(std::vector<std::string> const& v) { 
    std::vector<std::string>::const_iterator cit = 
     v.begin(); 
    for(;cit != v.begin(); ++cit) { 
     AddChar(*cit); 
    } 
} 

這不是一個真正舒適雖則通過。你必須手動構建你的矢量,然後通過...我發現你已經對可變參數樣式函數有了正確的感覺。一個人不應該將它們用於這種類型的代碼,只有在與C代碼或調試功能進行接口連接時纔會使用它們。處理這種情況的另一種方法是應用預處理器編程。這是一個高級話題,而且很冒險。這個想法是自動產生過高達到一定的上限,大致如下:

#define GEN_OVERLOAD(X) \ 
void AddChars(GEN_ARGS(X, std::string arg)) { \ 
    /* now access arg0 ... arg(X-1) */ \ 
    /* AddChar(arg0); ... AddChar(arg(N-1)); */ \ 
    GEN_PRINT_ARG1(X, AddChar, arg) \ 
} 

/* call macro with 0, 1, ..., 9 as argument 
GEN_PRINT(10, GEN_OVERLOAD) 

這是僞代碼。您可以看看升壓預處理器庫here

接下來的C++版本將提供更好的可能性。初始化列表可用於:

void AddChars(initializer_list<std::string> ilist) { 
    // range based for loop 
    for(std::string const& s : ilist) { 
     AddChar(s); 
    } 
} 

... 
AddChars({"hello", "you", "this is fun"}); 

這也是在下一個C++可能以支持使用variadic templates任意許多(混合型)的參數。 GCC4.4將支持他們。 GCC 4.3已經部分支持它們。

3

在這種情況下將 運算符重載是否是一種很好的做法?

我不這麼認爲。對於不知道自己超負荷運營商的人來說,這太令人困惑了。只要堅持描述性的方法名稱,忘記你正在輸入的額外字符,它只是不值得。你的維護者(或你自己在6個月內)會感謝你。

+0

我同意。運算符重載可能很酷,但是當你在幾個月後試圖維護代碼時,即使你編寫它,你也希望能夠以最小的努力理解它的功能。清晰的代碼比混亂的酷代碼更好。 – 2009-03-02 04:02:18

+0

我同意。只要你不改變操作員的意思。 I.E. MyWebRequest =「url」;不應該實際執行Web請求。對於這樣的事情,我更喜歡函數鏈或函數獲取數組。 – Rahly 2016-08-24 00:39:56

3

1)是的,除了AddChar是公開的,沒有理由需要成爲friend

2)這是有爭議的。 <<在某種程度上是一個運營商,它對於「怪異」事物的重載至少被勉強接受。

3)沒什麼明顯的。與往常一樣,剖析是你的朋友。您可能需要考慮通過const引用(const std::string&)將字符串參數傳遞給AddCharoperator<<以避免不必要的複製。

+0

對於std :: string引用+1,以避免複製。還有其他一切。 – Frank 2009-03-02 04:14:04

+0

謝謝。但是如果我刪除朋友,編譯器會拋出錯誤。 – 2009-03-02 04:16:59

+0

的確,我也給你+1了。我特別喜歡#1。但我會說清楚:「讓它成爲非會員非朋友功能」:)並且我也喜歡#2。而不是說「糟糕的壞」你提供了參數的基礎:)而且我認爲op <<被廣泛接受爲插入操作。 – 2009-03-02 04:19:17

2

我不希望這樣超負荷往心裏去,因爲載體通常不會有一個重載的左移運算符 - 它不是真正的它的成語;-)

我可能會從AddChar返回一個參考,而不是像這樣:

ExtendedVector& AddChar(const std::string& str) { 
    container.push_back(str); 
    return *this; 
} 

所以你可以再做

container.AddChar("First").AddChar("Second"); 

這是不是真的比位移操作符大得多。

(另請參閱Logan關於通過引用而不是按值傳遞字符串的評論)。

+0

你打敗我了!通過,呃,幾分鐘......;) – Frank 2009-03-02 04:17:29

1

運算符在這裏沒有被正確地重載。沒有理由讓操作員成爲朋友,因爲它可以是班級的成員。朋友是用於不是該類的實際成員的函數(例如,爲ostream超載< <,以便對象可以輸出到cout或ofstreams)。

你真正想要的操作是什麼:

ExtendedVector& operator<<(const std::string str){ 
    AddChar(str); 
    return *this; 
} 

它通常被認爲是不好的做法,超負荷運營商有他們做比他們做的事情,通常的方式。 < <通常是位移,因此以這種方式重載可能會造成混淆。很明顯,STL爲「流式插入」重載< <,因此可能對於以類似的方式使用它而言是有意義的。但是這看起來不像你在做什麼,所以你可能想避免它。

由於運算符重載與常規函數調用相同,因此調用是隱藏的,因爲它由編譯器自動完成,所以沒有性能問題。

2

在這種情況下,運算符重載不是很好的做法,因爲它使代碼不易讀。標準std::vector不具備推送元素的原因。

如果你擔心主叫代碼太長,你可以代替重載運算符的考慮:

container.AddChar("First").AddChar("Second"); 

這將是可能的,如果你有AddChar()回報*this

有趣的是,你有這個toString()函數。在的情況下,一個operator<<輸出到一個流將是標準的東西,而不是!所以如果你想使用運營商使toString()功能operator<<

0

這會讓情況變得相當混亂,我會用同樣的語法的std :: CIN到一個變量:

std::cin >> someint;

"First" >> container;

這樣它至少是一個插入運營商。對我來說,當任何事情有一個< <重載的運營商,我希望它是輸出的東西。就像std :: cout。