譯自five popular myths about c++ --by Bjarne Stroustrup (2)

Myth 2: "C++ is an Object-Oriented Language"

c++ 是面向對象的語言


No. C++ supports OOP and other programming styles, but is deliberately not limited to any narrow view of “Object Oriented.” It supports a synthesis of programming techniques including object-oriented and generic programming. More often than not, the best solution to a problem involves more than one style (“paradigm”). By “best,” I mean shortest, most comprehensible, most efficient, most maintainable, etc.

不完全正確,c++ 不僅支持面向對象還支持其他編程方式,不能刻意地限制於面向對象狹隘的層面。它支持一套組合編程技術包括面向對象和泛型編程。通常情況下,一個問題最佳的解決方案涉及多種風格。我說的最佳,意思是代碼更簡潔,容易理解,更加高效,更容易維護等等


The “C++ is an OOPL” myth leads people to consider C++ unnecessary (when compared to C) unless you need large class hierarchies with many virtual (run-time polymorphic) functions – and for many people and for many problems, such use is inappropriate. Believing this myth leads others to condemn C++ for not being purely OO; after all, if you equate “good” and “object-oriented,” C++ obviously contains much that is not OO and must therefore be deemed “not good.” In either case, this myth provides a good excuse for not learning C++.

這種觀點使人們認爲 c++ 相對 c 來說,不是那麼必須,除非你需要一個龐大的類層次,並且帶有許多虛函數(運行時多態)。對於許多人和許多問題來說,這樣使用並不合適。這個流言導致人們的譴責 c++ ,因爲它的面向對象不夠徹底。畢竟,如果你認爲好就是面向對象,顯然 c++ 包含更多的非面向對象的東西,因此被認爲是不好的,這也爲不要學習c++ 提供了一個好的藉口。


Consider an example:

考慮這樣一個例子

void rotate_and_draw(vector<Shape*>& vs, int r)
{
<span style="white-space:pre">	</span>for_each(vs.begin(),vs.end(), [](Shape* p) { p->rotate(r); });  // rotate all elements of vs
<span style="white-space:pre">	</span>for (Shape* p : vs) p->draw();                                  // draw all elements of vs
}

Is this object-oriented? Of course it is; it relies critically on a class hierarchy with virtual functions. It is generic? Of course it is; it relies critically on a parameterized container (vector) and the generic function for_each. Is this functional? Sort of; it uses a lambda (the [] construct). So what is it?  It is modern C++: C++11.

這是面向對象嗎?當然是,它很大程度上依賴帶有虛函數的類層次結構。它是泛型嗎?當然是拉,它同樣依賴於參數化的模板容器 vector 和泛型函數 for_each.它是函數式的嗎?它使用了 lambda 表達式,這點來說也算是。那麼它到底是什麼類型的?它就是現代的c++,c++11.


I used both the range-for loop and the standard-library algorithm for_each just to show off features. In real code, I would have use only one loop, which I could have written either way.

我同時使用了 範圍 for 循環和標準庫的算法 for_each ,僅僅是爲了展示一下這個特性,實際中,我只會用一種循環,用另一種寫法


Generic Programming

泛型編程


Would you like this code more generic? After all, it works only for vectors of pointers to Shapes. How about lists and built-in arrays? What about “smart pointers” (resource-management pointers), such as shared_ptr and unique_ptr? What about objects that are not called Shape that you can draw() and rotate()? Consider:
你想讓這段代碼再通用一點嗎(模版化,泛型)?畢竟,它只是用於形狀的容器指針。列表和內置數組會怎樣呢?像 shared_ptr 和 unique_ptr 的智能指針呢?那些不叫 Shape 的類可以用 draw() 和 rotate() 嗎?想一想:
template<typename Iter>
void rotate_and_draw(Iter first, Iter last, int r)
{
<span style="white-space:pre">	</span>for_each(first,last,[](auto p) { p->rotate(r); });  // rotate all elements of [first:last)
<span style="white-space:pre">	</span>for (auto p = first; p!=last; ++p) p->draw();       // draw all elements of [first:last)
}


This works for any sequence you can iterate through from first to last. That’s the style of the C++ standard-library algorithms. I used auto to avoid having to name the type of the interface to “shape-like objects.” That’s a C++11 feature meaning “use the type of the expression used as initializer,” so for the for-loop p’s type is deduced to be whatever type first is. The use of auto to denote the argument type of a lambda is a C++14 feature, but already in use.

這段代碼適用於任何可以從頭到尾迭代的序列。這就是c++ 標準庫算法的風格。我使用了 auto 關鍵字避免爲類似 Shape 對象的接口類型命名。這是c++11的一個特性,意思是使用表達式的類型作爲初始化類型,對於 for 循環來說,指針 p 的類型是由 Iter first 的類型得出的。使用 auto 表示 lambda 表達式參數的類型,是c++14的特徵,但是現在已經可以用了。


Consider:
思考一下:

void user(list<unique_ptr<Shape>>& lst, Container<Blob>& vb)
{
<span style="white-space:pre">	</span>rotate_and_draw(lst.begin(),lst.end());
<span style="white-space:pre">	</span>rotate_and_draw(begin(vb),end(vb));
}

Here, I assume that Blob is some graphical type with operations draw() and rotate() and that Container is some container type. The standard-library list (std::list) has member functions begin() and end() to help the user traverse its sequence of elements. That’s nice and classical OOP. But what if Container is something that does not support the C++ standard library’s notion of iterating over a half-open sequence, [b:e)? Something that does not have begin() and end() members? Well, I have never seen something container-like, that I couldn’t traverse, so we can define free-standing begin() and end() with appropriate semantics. The standard library provides that for C-style arrays, so if Container is a C-style array, the problem is solved – and C-style arrays are still very common.

在這段代碼裏,我假設 Bolb 是一個圖像類型,帶有draw() and rotate(),Container 是任意的容器類型。標準庫的 list 有2個成員函數 begin() end() ,可以用於函數 user 遍歷它序列中元素。這是典型的 面向對象編程。但是,如果類型 Container 不支持 c++ 標準裏半開區間的迭代概念呢?或者沒有 begin() end()的成員呢?當然,我從沒見過容器類型不能遍歷,那麼我們可以自定義合適的 begin() end().標準庫爲 c 風格的數組提供了上面的成員,所以即便 Container 是c 風格的數組,問題也可以解決,c 風格的數組仍然常用。


Adaptation

適用性


Consider a harder case: What if Container holds pointers to objects and has a different model for access and traversal? For example, assume that you are supposed to access a Container like this
思考一個複雜的情況,如果 Container 存儲對象的指針,有一套不同訪問和遍歷方式。舉例,假設你可以這樣訪問 Container 的元素
for (auto p = c.first(); p!=nullptr; p=c.next()) { /* do something with *p */}

This style is not uncommon. We can map it to a [b:e) sequence like this
這種樣式不常見,我們將區間指針做映射像下面這樣

template<typename T> struct Iter {
<span style="white-space:pre">	</span>T* current;
<span style="white-space:pre">	</span>Container<T>& c;
};

template<typename T> Iter<T> begin(Container<T>& c) { return Iter<T>{c.first(),c}; }
template<typename T> Iter<T> end(Container<T>& c)   { return Iter<T>{nullptr}; }
template<typename T> Iter<T> operator++(Iter<T> p)  { p.current = c.next(); return this; }
template<typename T> T*      operator*(Iter<T> p)   { return p.current; }

Note that this is modification is nonintrusive: I did not have to make changes to Container or some Container class hierarchy to map Container into the model of traversal supported by the C++ standard library. It is a form of adaptation, rather than a form of refactoring.

注意這個修改是無關緊要的,我並沒有爲了把容器映射成c++ 標準庫支持的迭代的模型而改寫容器或容器類的層次機構。這只是一種改寫的形式並不算重構。


I chose this example to show that these generic programming techniques are not restricted to the standard library (in which they are pervasive). Also, for most common definitions of “object oriented,” they are not object-oriented.

我選擇這個例子是爲了說明泛型編程技術並不只在標準庫中廣泛使用。對於一些很普通的面向對象的定義,其實他們並不是面向對象的


The idea that C++ code must be object-oriented (meaning use hierarchies and virtual functions everywhere) can be seriously damaging to performance. That view of OOP is great if you need run-time resolution of a set of types. I use it often for that. However, it is relatively rigid (not every related type fits into a hierarchy) and a virtual function call inhibits inlining (and that can cost you a factor of 50 in speed in simple and important cases).
c++ 必須是面向對象(層次結構和虛函數的濫用)的想法會嚴重危害到性能評價。如果你需要運行時解決一組類型時,OOP是非常棒的。我經常這樣用。但是它相對也比較死板(不是所有相關的類型都剛好嵌入同一層次結構)而且虛函數會抑制內聯(在處理簡單重要的工作時,這回大大增加耗時)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章