译自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是非常棒的。我经常这样用。但是它相对也比较死板(不是所有相关的类型都刚好嵌入同一层次结构)而且虚函数会抑制内联(在处理简单重要的工作时,这回大大增加耗时)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值