簡體   English   中英

我什么時候應該在C ++中使用引用?

[英]When should I use references in C++?

我一直在編寫C ++編程,我開始懷疑規則使用引用應盡可能應用於任何地方。

這個相關的SO帖子不同,我對一種不同的東西感興趣。

根據我的經驗,參考/指針混合會混淆你的代碼:

std::vector<Foo *> &x = get_from_somewhere(); // OK? reference as return value
some_func_pass_by_ref(x); // OK reference argument and reference variable
some_func_by_pointer(x[4]); // OK pointer arg, pointer value
some_func_pass_elem_by_ref(*x[5]); // BAD: pointer value, reference argument
some_func_that_requires_vec_ptr(&x); // BAD: reference value, pointer argument

一種選擇是,以取代&* constFoo &Foo * const

void some_func_by_ref(const std::vector<Foo * const> * const); // BAD: verbose!

這樣至少走遍了。 而且我重寫函數頭文件已經不見了,因為所有參數都是指針......代價是使用const而不是指針算法(主要是&* )來污染代碼。

我想知道在可能的規則下如何以及何時應用使用引用

考慮:

  • 最小化函數原型的重寫(即:哦,該死的我需要重寫很多原型,因為我想將這個引用的元素放入容器中)
  • 提高可讀性

    • 避免應用*Foo*轉換為Foo& ,反之亦然
    • 避免過多的const使用,如* const

注意:我想要的一件事是每當我打算將元素放入STL容器時使用指針(參見boost :: ref)

我認為這不是C ++ 03特有的東西,但如果它們可以被反向移植到C ++ 03(即:NRVO而不是移動語義),那么C ++ 11解決方案就可以了。

我什么時候應該在C ++中使用引用?

當你需要處理像對象本身這樣的變量時(大多數情況下你不明確需要指針而不想取得對象的所有權)。

我想知道在可能的規則下如何以及何時應用使用引用

盡可能 ,除非您需要

  • 處理地址(記錄地址,診斷或編寫自定義內存分配等)
  • 獲取參數的所有權(按值傳遞)
  • 尊重需要指針的接口(C互操作性代碼和遺留代碼)。

Bjarne Stroustrup在他的書中指出他引入了對該語言的引用,因為需要在不制作對象副本的情況下調用運算符(這意味着“通過指針”)並且他需要尊重類似於按值調用的語法(這意味着“不是通過指針”)(因此參考文獻誕生了)。

簡而言之,您應該盡可能少地使用指針:

  • 如果值是可選的(“可以為null”),則在它周圍使用std :: optional,而不是指針
  • 如果您需要獲取值的所有權,請按值接收參數(而不是指針)
  • 如果您需要在不修改值的情況下讀取值,請通過const &接收參數
  • 如果你需要動態分配或返回新的/動態分配的對象,通過以下之一傳輸值: std::shared_ptrstd::unique_ptryour_raii_pointer_class_here - 而不是(原始)指針
  • 如果你需要傳遞指向C代碼的指針,你仍然應該使用std :: xxx_ptr類,並使用.get()獲取指針以獲取原始指針。

我想到的一件事是每當我打算將元素放入STL容器時使用指針(或者我可以擺脫它嗎?)

您可以使用Boost指針容器庫

恕我直言,因為原始指針很危險,因為所有權和破壞責任很快就不清楚了。 因此,圍繞概念的多個封裝( smart_ptrauto_ptrunique_ptr ,...)。 首先,考慮在容器中使用這樣的封裝而不是原始指針。

其次,為什么需要將指針放在容器中? 我的意思是,它們意味着包含完整的物體; 畢竟,他們有一個allocator作為模板參數,用於精確的內存分配。 大多數情況下,你需要指針,因為你有一個OO方法大量使用多態。 你應該重新考慮這種方法。 例如,您可以替換:

struct Animal {virtual std::string operator()() = 0;};
struct Dog : Animal {std::string operator()() {return "woof";}};
struct Cat : Animal {std::string operator()() {return "miaow";}};
// can not have a vector<Animal>

通過類似的方式,使用Boost.Variant

struct Dog {std::string operator()() {return "woof";}};
struct Cat {std::string operator()() {return "miaow";}};
typedef boost::variant<Dog, Cat> Animal;
// can have a vector<Animal>

這樣,當您添加新動物時,您不會繼承任何內容,只需將其添加到變體即可。

使用Boost.Fusion時 ,您也可以考慮使用更復雜但更通用的東西:

struct Dog {std::string talk; Dog() : talk("wook"){}};
struct Cat {std::string talk; Cat() : talk("miaow"){}};

BOOST_FUSION_ADAPT_STRUCT(Dog, (std::string, talk))
BOOST_FUSION_ADAPT_STRUCT(Cat, (std::string, talk))

typedef boost::fusion::vector<std::string> Animal;

int main()
{
    vector<Animal> animals;
    animals.push_back(Dog());
    animals.push_back(Cat());

    using boost::fusion::at;
    using boost::mpl::int_;

    for(auto a : animals)
    {
        cout << at<int_<0>>(a) << endl;
    }
}

這樣你甚至不需要修改像變種這樣的集合,也不需要修改動物上的算法,你只需要提供一個與所用算法先決條件相匹配的FUSION_ADAPT。 兩個版本(變體和融合)都允許您定義正交對象組,這是繼承樹無法做到的有用事情。

處理此問題的方法似乎合理:

  • boost和C ++ 11有一個可以廉價地用於在容器中存儲引用的類: Reference Wrapper
  • 一個好的建議是更頻繁地使用句柄/身體習語而不是傳遞原始指針。 這也解決了由引用或指針控制的內存的所有權問題。 來自Adobe的Sean Parent2013年的本土演講中指出了這一點。

我選擇使用Handle / Body Idiom方法,因為它在隱藏底層實現和所有權語義的同時提供指針自動復制/分配行為。 它還可以作為一種編譯時防火牆,減少頭文件包含。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM