׃如书中的SampleQ关于时间的Q我们一般的做法是在创?/span>Day对象Ӟq加校验函数来判断年月日是不是有效?/span>
定义Q?/span>
class Month {
public:
static Month Jan() {return Month(1); }
static Month feb() {return Month(2); }
…
private Month(int m);
};
Date d(Month::Mar(), Day(30), Year(1995) );
为啥不直接用静态变量?
参?/span>Item4 P30Q简单说是不能保证在?/span>non-local static objectsӞq个对象已l初始化了。如?/span>non-local static objects在另一个文件了Q又恰y没有初始化,pȝ当然׃(x)辫子了?/span>
先参?/span>Item3 P19
class Rational { …};
const Rational operator*(const Rational &lhs, const Rational &rhs);
之所以强制设|ؓ(f)const是Z避免client在用时出错。因为如果没?/span>clienQ那么:(x)
Rational a,b,c;
…
(a*b)=c
q种写法是对的,但是如果aQ?/span>b是内|类型,q种写法是错误的?/span>
除非有必要,否则p保证你的cdtype的行为和内置cd一致?/span>
STL是榜Pjava在这里成了反面教材,因ؓ(f)如果想知道容器内对象的数量,?/span>ArrayQ要讉K属?/span>lengthQ?/span>String要用length函数Q?/span>ArrayList要用size函数Q这是不一致性?/span>
扑և以下写法的两个易错的地方Q?/span>
Investment* createInvestment();
1 忘记删除createInvestment()q回的指?/span>
2 删除q个指针多次
soQ修改定义:(x)
std::tr1::shared_ptr< Investment > createInvestment();
如果出现q种情ŞQ从createInvestment得到Investment*的函数要把这个指针传递个l叫?/span>getRidOfInvestmentQ由getridOfInvestment取代使用delete?/span>
q里出C一个新?/span>client易错的点Q用h怼(x)使用错的资源释放机制。因?/span>delete?/span>getRidOfInvestment取代了?/span>
std::tr1::shared_ptr< Investment >
pInv(static_cast<Investment*>(0), getRidOfInvestment);
那么定义应该是q样的:(x)
std::tr1::shared_ptr< Investment > createInvestment()
{
std::tr1::shared_ptr< Investment >
retVal(static_cast<Investment*>(0), getRidOfInvestment); //q不能让client来做
retVal = …;
return retVal;
}
tr1::shared_ptr的优Ҏ(gu)允许在一?/span>DLL创徏对象Q在另一?/span>DLL里删除对象?/span>
在设计一个类的时候,要回{一pd的问题哦?/span>
参考大师在P85-P86之间l出的常常的清单吧,其实实际上,我在设计cȝ时候的没有想q这么多Q问q自p么的Z么,所以这也是我L在追求代码重用,却L发现自己写的代码重用度很低的一个原因把?/span>
But先学?fn)一个单词,characteristicQ?/span>KAOQ这竟然是个名词?/span>
再学一个地道的说法Q解决问题的Ҏ(gu)Q?/span>The way around the slicing problem is…
函数都是g递?/span>pass by-value?/span>function parameters are initialized with copies of the actual arguments, and function callers goes back a copy of the value returned by the function.q样当然开销大了,每次都先copy一份进来,完事以后Q再copy一份出厅R?/span>
假设函数的参数是一?/span>Student对象Q?/span>bool validateStudent(Student s);调用q个函数Q?u>额外的隐性开销包括要先调用copy constructor创徏一?/span>Student对象用于函数内部Q函数执行结束再调用析构函数释放q个对象?/span>
bool validateStudent(const Student& s);
引用是通过指针来实现实现的Q因此传递引用实际上是在传递指针?/span>references are typically implemented as pointers.
但是q个规则对于内置数据cd不适用Q也不是适用STL iterator和函数对?/span>function objects?/span>
即再小的对象也应该不要使用g递,而是要?/span>pass by reference-to-const?/span>
slicing problem是在多态规则里面容易生的?/span>
看一个简单的基类、派生类的定?/span>
class Window
{
public:
int height;
int width;
};
class TextWindow : public Window
{
public:
int cursorLocation;
};
…
Window win;
TextWindow *tWinPtr;
tWinPtr = new TextWindow;
win = *tWinprt;
win是一?/span>Window对象Q?/span>C++规定Q给win分配的内存看见的大小Q由光态类型决定。就是说默认的拷贝函数导致信息会(x)出现丢失。这是slicing problem?/span>
试想一下这要是通过g递的方式传递参敎ͼ实参一copy已l丢׃息了?/span>
堆和栈这?/span>2个不同的概念Q哎哟,我一直以为是一个词?/span>
heapQ堆
stackQ栈
argument
(against) 争论Q意?/span>
实参
形参?/span>parameters
q是一个典型的例子Q?/span>
class WebBrowser {
public:
…
void clearCache();
void clearHistory();
void removeCookies();
…
};
Z提供一个执行所有操作的函数Q所以就?/span>WebBrowser里面q加定义Q?/span>
void clearEverything();
哎,我一直就是这么写的,q自以ؓ(f)有很好的装Q?/span>But
void clearBrowser(WebBrowser wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
W一Q前者ƈ不比后者有很好的封?/span>
q就要解释一下什么叫?#8220;装”Q以及封装的判别标准?/span>
装的判别标准:(x)可以通过l计能够讉Kq个data的函数的数目来计,函数多Q这?/span>data装也就月不好,因此前一U写法的装没有后者好。这也可以用来解?/span>Item22里面Qؓ(f)什么要求数据成员不能定义ؓ(f)public。另外增?/span>clearEverything()作ؓ(f)member functionQ实际上是降低了装性。而后面的non-member non-friend functions的定义就没有改变WebBrowser的封装性?/span>
W二Q后者还能提供更加灵zȝ打包packageQ增加扩展性?/span>
put all convenience functions in multiple header files, but one namespace.
W三Q增加函数的可扩展性?/span>
你可以定义自qconvenience functionsQ写C?/span>header file里面Q放到同一?/span>namespace里面。这?/span>member function做不到的?/span>
原因Q?/span>
Parameters are eligible for implicit type conversion only if they are listed in the parameter list.
l论Q?/span>
make operator* a non-member function, thus allowing compilers to perform implicit type conversions on all arguments.
class Rational {
…
};
const Rational operatior*(const Rational& lhs, Rational& rhs)
{
return Rationan(lhs.numerator()*rhs.numerator(),
lhs.denominator()*rhs. denominator () );
}
一个误区:(x)
如果一个函敎ͼ和某个类相关Q而又不能定义?/span>memberQ那么这个函数就一定要定义?/span>friend?/span>
上面q个例子p明这个说法ƈ不正。真q命,慎用friend functions?/span>
是?/span>std里面定义?/span>swap
WidgetQ?/span>:我们希望的是交换指针Q但swap实际做的是不?/span>copy?/span>3?/span>Widget对象Q而且q?/span>copy?/span>3?/span>WidgetImpl对象。太费了!都低x代了?/span>
class Widget{
void swap(Widget& other)
{
using std::swap;
swap(pImpl, other.pImpl;);
}
…
};
template<> void swap<Widget>( Widget& a, Widget&b)
{
a.wap(b);
}
接下来要讨论的是如果Widget?/span>WidgetImpl不是c而是cL板会(x)怎么P
U束条gQ不能在std里面增加新的templateQ只能特化(specializeQ?/span>std内的template?/span>
如果非要定义Q?/span>say sorry?/span>behavior is undefined?/span>KAOQ其实这比异常还讨厌?/span>
解决Ҏ(gu)是把它定义到一个自qnamespace里面Q而不要定义到std里面?/span>
namespace WidgetStuff {
…
template<typename T>
class Widget{…};
…
template<typename T>
void swap(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
如果仅仅是针对一?/span>classQ那q?/span>std::swap?/span>
If you want to have your class-specializing version of swap called in as many contexts as possible, you need to write both a non-member version in the same namespace as your class and a specialization of std::swap.
q部分十分绕Q?/span>P111q对?/span>C++?/span>name lookup的规则进行了详细的描q。值得重新温习(fn)?/span>