[note] Effective C++ Chapter 4
13406 ワード
Effective C++ Learning Note 4 (undergradute edition)
Designs and Declarations
===========================================
we now only treat the possible meaningful advice which can work for classes
easy and clear Interface with high-tolerance of client errors
type system
Many client errors can be prevented by the introduction of new types.
e.g. in the Date class, we can encapsule the y,m,d in different classes
aside from that, to guarantee the type safety while month is define in [1, 12], we could use functions like
my thinking about the static function to replace a non-local static obj: when we init the whole class’s obj, its static objs may haven’t been created yet. it’s a compatible process.
restrict the behavior of clients Add Avoiding gratuitous incompatibilities with the built-in types: such as using Automatically
Treat classes as types
Designing good classes is challenging because designing good types is challenging. created and destroyed: ctors, dtors, new, delete initialization differ from object assignment passed by value: copy constructor defines how pass-by-value is implemented for a type. restrictions on legal values(in the former item we have just talked about) fit into an inheritance graph type conversions: even a specific conver-function the compatibility with existing functions and objs; standard functions should be disallowed? (copy disallow) interfaces design meaningful?: general and necessary.
prefer
aside from built-in types,
why?
to avoid repeated invacation of ctors and dtors. while remain the master copy unchanged.
effectively solve the problem of base-slicing
which happened when we pass a derived obj to a base ptr
obj of small types sometimes contains not much more than a pointer(which is the impl. of reference), but if we pass by reference, we’d call ctors of all of the units it points to.
issues of “unnecessary”
small user-class may not perform well dealing with pass-by-val (this is essentially connected to the reason why we pass by reference, not just superficially for being small)
the small class will be enlarged
No reference when return an obj
here don’t be entangled with the efficiency problem,
when deciding between returning a reference and returning an object, your job is to make the choice that offers correct behavior.
some bad code ex:
Declare data members syntactic consistency: clients just need to call by fine-grained access control: readOnly, writeOnly and readWrite. encapsulation
encapsulation
thus, we can say that
Prefer non-member non-friend functions to member functions
the non-member non-friend function yields greater encapsulation in the class than a member or friend function. it can only integrate functions which have been defined, thus with higher restriction.
tips: First, this reasoning applies only to non-member non-friend 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(such as a utility class). what we want is high encapsulation
functions stated above should be declared in one namespace(where functions it calls are declared)
Declare non-member functions when type conversions should apply to all parameters
sometimes we need implicit conversions. such as a rational-computing class
from the bug above,
from this example, we could see that, if member functions cannot solve the problem, friend function may not be necessary.
(about friend function)Whenever you can avoid friend functions, you should, because, much as in real life, friends are often more trouble than they’re worth.
effective
related to templates. thus, we don’t talked about it too much
we just talked about the pimpl idiom: to declare a pimpl(pointer to implementation), and if the class is too large, we just need to swap the pointer.
Designs and Declarations
===========================================
we now only treat the possible meaningful advice which can work for classes
easy and clear Interface with high-tolerance of client errors
type system
Many client errors can be prevented by the introduction of new types.
e.g. in the Date class, we can encapsule the y,m,d in different classes
struct Day
{
explict Day(int d): val(d){ };
int val;
};
//and then we can confine the client's attempt to limited range like this:
Date d(Month(3), Day(30), Year(1995));
aside from that, to guarantee the type safety while month is define in [1, 12], we could use functions like
static Month Jan()
(this avoid the problematic non-local static initialization.) my thinking about the static function to replace a non-local static obj: when we init the whole class’s obj, its static objs may haven’t been created yet. it’s a compatible process.
restrict the behavior of clients
const
. in item 3, we talked about the if(a*b=c)
problem, size()
for all DT in STL. delete
(or sth like this) for client Treat classes as types
Designing good classes is challenging because designing good types is challenging.
prefer
const&
aside from built-in types,
iterator
s, functional
s, this is a very important and useful rules. why?
to avoid repeated invacation of ctors and dtors. while remain the master copy unchanged.
effectively solve the problem of base-slicing
which happened when we pass a derived obj to a base ptr
obj of small types sometimes contains not much more than a pointer(which is the impl. of reference), but if we pass by reference, we’d call ctors of all of the units it points to.
issues of “unnecessary”
small user-class may not perform well dealing with pass-by-val (this is essentially connected to the reason why we pass by reference, not just superficially for being small)
the small class will be enlarged
No reference when return an obj
here don’t be entangled with the efficiency problem,
when deciding between returning a reference and returning an object, your job is to make the choice that offers correct behavior.
some bad code ex:
//friend function: in stack(deleted then)
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
Rational result(lhs.n*rhs.n, lhs.d*rhs.d);
return result;
}
//allocate in heap by 'new': leakage
const Rational& operator*(const Rational &lhs, const Rational &rhs)
{
Rational *result = new Rational(lhs.n*rhs.n, lhs.d*rhs.d);
return *result;
}
//static obj with global life: there is only ONE obj
const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
static Rational result;
result = ...;
return result;
}
//in the last case, we may think of putting static into vector...there'll still be the cost of dtors and ctors, also global life means some memory unnecessrily occupied, underlying waste in the long run.
// a possible version
inline const Rational operator*(const Rational &lhs, consr Rational &rhs)
{
return Rational(...);
}
it act like the 'static array', while delete unnecessary ones in time
Declare data members
private
name()
s encapsulation
private
is the highest control: if we declare a var protected
or public
, then we eliminate or change it back to private
, it’ll be disastrous (piles of codes to rewrite, retest, redocument or recompile) it affords us the flexibility to change things in a way that affects only a limited number of clients. thus, we can say that
protected
is no more encapsulated than public
. Prefer non-member non-friend functions to member functions
the non-member non-friend function yields greater encapsulation in the class than a member or friend function. it can only integrate functions which have been defined, thus with higher restriction.
tips:
functions stated above should be declared in one namespace(where functions it calls are declared)
Declare non-member functions when type conversions should apply to all parameters
sometimes we need implicit conversions. such as a rational-computing class
class Rational {
public:
...
const Rational operator*(const Rational& rhs) const;
};
Rational a(1, 8);
Rational ans1 = a * 2 // right
Rational ans2 = 2 * a // wrong
from the bug above,
int
won’t automatically converse to Rational
to solve that, we’d created a function that has two param(where the two obj could be conversed to Rational
objs) from this example, we could see that, if member functions cannot solve the problem, friend function may not be necessary.
(about friend function)Whenever you can avoid friend functions, you should, because, much as in real life, friends are often more trouble than they’re worth.
effective
std::swap()
related to templates. thus, we don’t talked about it too much
we just talked about the pimpl idiom: to declare a pimpl(pointer to implementation), and if the class is too large, we just need to swap the pointer.