码迷,mamicode.com
首页 > 编程语言 > 详细

<Effective C++>读书摘要--Designs and Declarations<三>

时间:2015-04-22 00:15:26      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:

<Item 22> Declare data members private

1、使数据成员private,保持了语法的一致性,client不会为访问一个数据成员是否需要使用括号进行函数调度,还是不使用括号直接访问成员而纠结。

2、使数据成员private,if you use functions to get or set its value, you can implement no access, read-only access, and read-write access. Heck, you can even implement write-only access if you want to:

class AccessLevels {
public:
  ...
  int getReadOnly() const        { return readOnly; }
  void setReadWrite(int value)   { readWrite = value; }
  int getReadWrite() const       { return readWrite; }
  void setWriteOnly(int value)   { writeOnly = value; }
private:
  int noAccess;                         // no access to this int
  int readOnly;                         // read-only access to this int
  int readWrite;                        // read-write access to this int
  int writeOnly;                        // write-only access to this int
};

3、Still not convinced? Then it‘s time to bring out the big gun: encapsulation. If you implement access to a data member through a function, you can later replace the data member with a computation, and nobody using your class will be any the wiser.

4、The argument against protected data members is similar. In fact, it‘s identical, though it may not seem that way at first. The reasoning about syntactic consistency and fine-grained access control is clearly as applicable to protected data as to public, but what about encapsulation? Aren‘t protected data members more encapsulated than public ones? Practically speaking, the surprising answer is that they are not.Item 23 explains that something‘s encapsulation is inversely proportional to the amount of code that might be broken if that something changes. The encapsulatedness of a data member, then, is inversely proportional to the amount of code that might be broken if that data member changes。

 5、Suppose we have a public data member, and we eliminate it. How much code might be broken? All the client code that uses it, which is generally an unknowably large amount. Public data members are thus completely unencapsulated. But suppose we have a protected data member, and we eliminate it. How much code might be broken now? All the derived classes that use it, which is, again, typically an unknowably large amount of code. Protected data members are thus as unencapsulated as public ones, because in both cases, if the data members are changed, an unknowably large amount of client code is broken. This is unintuitive, but as experienced library implementers will tell you, it‘s still true. Once you‘ve declared a data member public or protected and clients have started using it, it‘s very hard to change anything about that data member. Too much code has to be rewritten, retested, redocumented, or recompiled. From an encapsulation point of view, there are really only two access levels: private (which offers encapsulation) and everything else (which doesn‘t). 比private更彻底的方案是把私有部分放在一个私有的内部类中,并把私有类的定义从公开的头文件中删掉,只被实现所见,这个方案性能损失很小,重构这个类时,不仅不需要修改使用这个类的代码,甚至不需要重新编译使用这个类的代码。

6、Things to Remember

  • Declare data members private. It gives clients syntactically uniform access to data, affords fine-grained access control, allows invariants to be enforced, and offers class authors implementation flexibility.

  • protected is no more encapsulated than public. 

<Item 23> Prefer non-member non-friend functions to member functions

7、Object-oriented principles dictate that data and the functions that operate on them should be bundled together, and that suggests that the member function is the better choice. Unfortunately, this suggestion is incorrect. It‘s based on a misunderstanding of what being object-oriented means. Object-oriented principles dictate that data should be as encapsulated as possible. Counterintuitively, the member function clearEverything actually yields less encapsulation than the non-member clearBrowser. Furthermore, offering the non-member function allows for greater packaging flexibility for WebBrowser-related functionality, and that, in turn, yields fewer compilation dependencies and an increase in WebBrowser extensibility. The non-member approach is thus better than a member function in many ways. It‘s important to understand why.

class WebBrowser {
public:
  ...
  void clearCache();
  void clearHistory();
  void removeCookies();
  ...
};
//第一种方案 使用成员函数
class WebBrowser { public: ... void clearEverything(); // calls clearCache, clearHistory, // and removeCookies ... };
//另一种方案使用非成员函数
void clearBrowser(WebBrowser& wb) { wb.clearCache(); wb.clearHistory(); wb.removeCookies(); }

 

 8、The greater something is encapsulated, then, the greater our ability to change it. That‘s the reason we value encapsulation in the first place: it affords us the flexibility to change things in a way that affects only a limited number of clients.

 9、Consider the data associated with an object. The less code that can see the data (i.e., access it), the more the data is encapsulated, and the more freely we can change characteristics of an object‘s data, such as the number of data members, their types, etc. As a coarse-grained measure of how much code can see a piece of data, we can count the number of functions that can access that data: the more functions that can access it, the less encapsulated the data.我们应该精简模块的对外的接口,任何可以从其他接口实现出来的接口都是多余的,至少它们不应该在同一个层次上。

 10、At this point, two things are worth noting. First, this reasoning applies only to non-member non-friend functions. Friends have the same access to a class‘s private members that member functions have, hence the same impact on encapsulation. From an encapsulation point of view, the choice isn‘t between member and non-member functions, it‘s between member functions and non-member non-friend functions. (Encapsulation isn‘t the only point of view, of course. Item 24 explains that when it comes to implicit type conversions, the choice is between member and non-member functions.)

The second thing to note is that just because concerns about encapsulation dictate that a function be a non-member of one class doesn‘t mean it can‘t be a member of another class. This may prove a mild salve to programmers accustomed to languages where all functions must be in classes (e.g., Eiffel, Java, C#, etc.). For example, we could make clearBrowser a static member function of some utility class. As long as it‘s not part of (or a friend of) WebBrowser, it doesn‘t affect the encapsulation of WebBrowser‘s private members.

11、In C++, a more natural approach would be to make clearBrowser a non-member function in the same namespace as WebBrowser:

namespace WebBrowserStuff {
 class WebBrowser { ... };
 void clearBrowser(WebBrowser& wb);
  ...
}

 

12、This has more going for it than naturalness, however, because namespaces, unlike classes, can be spread across multiple source files.A class like WebBrowser might have a large number of convenience functions, some related to bookmarks, others related to printing, still others related to cookie management, etc. As a general rule, most clients will be interested in only some of these sets of convenience functions. There‘s no reason for a client interested only in bookmark-related convenience functions to be compilation dependent on, e.g., cookie-related convenience functions. The straightforward way to separate them is to declare bookmark-related convenience functions in one header file, cookie-related convenience functions in a different header file, printing-related convenience functions in a third, etc.:

// header "webbrowser.h" — header for class WebBrowser itself
// as well as "core" WebBrowser-related functionality
namespace WebBrowserStuff {
   class WebBrowser { ... };
     ...                                // "core" related functionality, e.g.
                                        // non-member functions almost
                                        // all clients need
}
// header "webbrowserbookmarks.h"
namespace WebBrowserStuff {
  ...                                   // bookmark-related convenience
}                                       // functions
// header "webbrowsercookies.h"
namespace WebBrowserStuff {
  ...                                   // cookie-related convenience
}                                       // functions
...

 

Note that this is exactly how the standard C++ library is organized.This allows clients to be compilation dependent only on the parts of the system they actually use. (See Item 31 for a discussion of other ways to reduce compilation dependencies.) Partitioning functionality in this way is not possible when it comes from a class‘s member functions, because a class must be defined in its entirety; it can‘t be split into pieces.

13、Putting all convenience functions in multiple header files — but one namespace — also means that clients can easily extend the set of convenience functions. All they have to do is add more non-member non-friend functions to the namespace. For example, if a WebBrowser client decides to write convenience functions related to downloading images, he or she just needs to create a new header file containing the declarations of those functions in the WebBrowserStuff namespace. 

14、Things to Remember

  • Prefer non-member non-friend functions to member functions. Doing so increases encapsulation, packaging flexibility, and functional extensibility.

<Item 24> Declare non-member functions when type conversions should apply to all parameters

15、You know that, say, multiplication of rational numbers is related to the Rational class, so it seems natural to implement operator* for rational numbers inside the Rational class. 

class Rational {
public:
  Rational(int numerator = 0,        // ctor is deliberately not explicit;
           int denominator = 1);     // allows implicit int-to-Rational
                                     // conversions
  int numerator() const;             // accessors for numerator and
  int denominator() const;           // denominator — see Item 22
  const Rational operator*(const Rational& rhs) const;
private:
  ...
};
result = oneHalf * 2;                             // fine
result = 2 * oneHalf;                             // error!

 

It turns out that parameters are eligible for implicit type conversion only if they are listed in the parameter list. The implicit parameter corresponding to the object on which the member function is invoked — the one this points to — is never eligible for implicit conversions. That‘s why the first call compiles and the second one does not. The first case involves a parameter listed in the parameter list, but the second one doesn‘t.

16、You‘d still like to support mixed-mode arithmetic, however, and the way to do it is by now perhaps clear: make operator* a non-member function, thus allowing compilers to perform implicit type conversions on all arguments:

class Rational {
  ...                                             // contains no operator*
};
const Rational operator*(const Rational& lhs,     // now a non-member
                         const Rational& rhs)     // function
{
  return Rational(lhs.numerator() * rhs.numerator(),
                  lhs.denominator() * rhs.denominator());
}
Rational oneFourth(
1, 4); Rational result; result = oneFourth * 2; // fine result = 2 * oneFourth; // hooray, it works!

 

17、Should operator* be made a friend of the Rational class?

In this case, the answer is no, because operator* can be implemented entirely in terms of Rational‘s public interface. The code above shows one way to do it. That leads to an important observation: the opposite of a member function is a non-member function, not a friend function. Too many C++ programmers assume that if a function is related to a class and should not be a member (due, for example, to a need for type conversions on all arguments), it should be a friend. This example demonstrates that such reasoning is flawed. Whenever you can avoid friend functions, you should, because, much as in real life, friends are often more trouble than they‘re worth. Sometimes friendship is warranted, of course, but the fact remains that just because a function shouldn‘t be a member doesn‘t automatically mean it should be a friend.

18、This Item contains the truth and nothing but the truth, but it‘s not the whole truth. When you cross the line from Object-Oriented C++ into Template C++ (see Item 1) and make Rational a class template instead of a class, there are new issues to consider, new ways to resolve them, and some surprising design implications. Such issues, resolutions, and implications are the topic of Item 46.

19、Things to Remember

  • If you need type conversions on all parameters to a function (including the one pointed to by the this pointer), the function must be a non-member.

<Item 25> Consider support for a non-throwing swap

20、

 

 

<Effective C++>读书摘要--Designs and Declarations<三>

标签:

原文地址:http://www.cnblogs.com/GloryLion/p/4445701.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!