标签:c++ effective c++ 语法优化
(五).实现
____________________________________________________________________________________________________________________________________
条款30:透彻了解inlining 的里里外外
#1.inline是将“对此函数的每一个调用”都以函数本体来替换之,所以inline函数inline void f(){...} //假设编译器有意愿inline "对f的调用” void (*pf)()=f; //pf指向f ... f(); //这个调用将被inlined,因为它是一个正常调用 pf(); //这个调用或许不被inlined,因为它通过函数指针达成(3).有时候编译器会生成构造函数和析构函数的副本,如此一来它们就可以获得指针
//注意,当你声明一个函数而它用到某个class时,你并不需要该 class 定义; //纵使函数以 by value 方式传递该类型的参数(或返回值): class Date; //Date声明式 Date today(); void clearAppointments(Date d); //Date定义式(3).为声明式和定义式(对同一对象的说法)提供不同的头文件
(用于声明式较多时,为了组织结构的统一) 例如: #include"datefwd.h" //其中声明 class Date; Date today(); void clearAppointments(Date d); //"datefwd.h"命名方式取决于C++标准程序库文件的<iosfwd>,<iosfwd>带来的 //另一个彰显是,本条款也使用于templates,毕竟也有些建置环境允许将templates //的定义式放在”非头文件“内,这样一来就可以将只含声明式的头文件提供给templates
//Handle classes示例: #include<string> #include<memory> //Person.h class PersonImpl; class Address; class Person{ public: Person(const std::string& name, const Date& birthday, const Address& addr); std::string name()const; std::string birthDate() const; std::string address() const; ... private: std::tr1::shared_ptr<PersonImpl> pImpl; }; //Person.cpp #include"PersonImpl.h" Person(const std::string& name, const Date& birthday, const Address& addr) :pImpl(new PersonImpl(name, birthday, addr)) {} std::string Person::name() const { return pImpl->name(); } //Interface classes示例: class Person{ public: virtual ~Person(); virtual std::string name() const = 0; virtual std::string birthDay() const = 0; virtual std::string address() const = 0; static std::tr1::shared_ptr<Person> //工厂函数(或称为virtual构造函数) create(const std::string& name, //创建一个派生类对象,并返回基类指针 const Date& birthday, const Address& addr); return std::tr1::shared_ptr<Person>(new RealPerson(name, birthday,addr)); ... }; class RealPerson:public Person{ public: RealPerson(const std::string& name, const Date& birthday, const Address& addr) :theName(name),theBirthDate(birthday),theAddress(addr) {} virtual ~RealPerson() std::string name() const; std::string birthDay() const; std::string address() const; private: std::string theName; Date theBirthDate; Address theAddress; }; {} //客户可以这样使用: std::string name; Date dateOfBirth; Address address; ... //创建一个对象,支持Person接口 std::tr1::shared_ptr<Person> pp(Person::create(name, dateOfBirth, address)); ... std::cout<<pp->name(), <<"was born on" <<pp->birthDay() <<"and now lives at" <<pp->address(); ... //pp离开作用域对象会自动消除对Handle classes和Interface classes的评价:
(六).继承与面向对象设计
____________________________________________________________________________________________________________________________________
class Bird{ public: virtual void fly(); //鸟会飞 ... }; class Penguin:public Bird{ //企鹅是一种鸟 ... }; //这里的企鹅是一种鸟使用了public继承, //但这里仍然有一个问题,”企鹅不会飞!“, //而此处的继承关系却表明企鹅会飞,之所以 //出错的原因在于并不是所有的鸟类都会飞。 //我们可以更改接口使它运行期产生错误: void error(const std::string& msg); //定义于另外某处 class Penguin:public Bird{ public: virtual void fly() { error("Attempt to make a penguin fly!"); } ... }; //如此便可以在运行期表明,“企鹅会飞,但尝试这么做却是一种错误!” //而好的接口应该可以防止无效的代码通过编译,因此我们应该采取 //另一种在“在编译器拒绝企鹅飞行”的设计,如下: class Bird{ ... }; class FlyingBird: public Bird{ public: virtual void fly(); ... }; class Penguin: public Bird{ ... }; //此时,因为行为的正确合理,你让企鹅飞,编译器肯定会抱怨: Penguin p; p.fly(); //编译期错误
//考虑以下代码: class Renctangle{ public: virtual void setHeight(int newHeight); virtual void setWidth(int newWidth); virtual int height() const; //返回当前值 virtual int width() const; ... }; void makeBigger(Rectangle& r) //这个函数用以增加r的面积 { int oldHeight = r.height(); r.setWidth(r.width() + 10); //为r的宽度增加10 assert(r.height() == oldHeight);//判断r的高度是否未曾改变 } //显然,上述的assert结果永远为真。因为makeBigger只改变r的宽度;r的 //高度从未改变。 //现考虑这段代码,其中使用public继承,允许正方形被视为一种矩形: class Square: public Rectangle {...}; Square s; ... assert(s.width() == s.height()); //这对正方形一定为真 makeBigger(s); //由于继承,s是一种(is-a)矩形, //所以我们可以增加其面积。 assert(s.width() == s.height()); //对所有正方形应该仍然为真。 //但我们遇到了一个问题,当makeBigger(s)之后,第二个assert就不为真了 //其原因是Square改变了base class-Rectangle的形态,对行为产生了约束, //因为它要求了长等于宽,这也正验证了本条款的这句话:public继承适合于 //不会对base classes 原有形态和行为产生束缚的derived class //从另一个角度来看,任何施用于base class的事情都应该施用于dervid class //而base class要求长可以不等于宽,但dervid class却无法适应该要求,因此, //咋看之下,正方形和长方形应该用public继承,但实则不然。____________________________________________________________________________________________________________________________________
class Base{ private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); ... }; class Derived:public Base{ public: virtual void mf1(); void mf3(); void mf4(); ... }; Derived d; int x; ... d.mf1(); //没问题,调用Derived::mf1 d.mf1(x); //错误!因为 Dervid::mf1遮掩了Base::mf1 d.mf2(); //没问题,调用Base::mf2 d.mf3(); //没问题,调用Derived::mf3 d.mf3(x); //错误!因为 Derived::mf3遮掩了Base::mf3 //【使用using声明式的方法】: class Derived:public Base{ public: using Base::mf1; //让 Base class内名为mf1 和 mf3的所有东西 using Base::mf3; //在 Derived 作用域内都可见(并且public) virtual void mf1(); void mf3(); void mf4(); ... }; //现在再看以下改变: d.mf1(); //没问题,调用Derived::mf1 d.mf1(x); //现在没问题,调用Base::mf1 d.mf2(); //没问题,调用Base::mf2 d.mf3(); //没问题,调用Derived::mf3 d.mf3(x); //现在没问题,调用Base::mf3 //现假设我们唯一想要继承Base::mf1() //因此我们可用【转交函数】的手法使其可见, //并使用该特定手法: class Derived:private { public: virtual void mf1(){Base::mf1();} //转交函数暗自成为inline ... }; Derived d; int x; d.mf1(); //很好,调用的是 Derived::mf1 d.mf1(x); //错误! Base::mf1(x)被遮掩了____________________________________________________________________________________________________________________________________
//考虑以下代码: class Shape { public: virtual void draw() const = 0; //<1>的形式 virtual void error(const std::string& msg); //<2>的形式 int objectID() const; //<3>的形式 ... }; class Rectangle:public Shape{...}; class Ellipse:public Shape{...};
//考虑以下代码: class Airport{...}; class Airplane{ public: virtual void fly(const Airport& destination); ... }; void Airplane::fly(const Ariport& destination); { //缺省实现,将飞机飞至指定的目的地 } class ModelA:public Airplane{...}; class ModelB:public Airplane{...}; //现假设该飞机场引进了C型飞机,但此C型飞机不走默认路线, //而是走新的航线,于是该航空公司程序员为其添加了一些代码, //但由于一时心急,却忘了添加新的fly函数: class ModelC:public Airplane{...}; //未声明fly函数 //但此代码编译行的通,运行也没问题,于是C型飞机仍走默认路线, //因此便酿成了该航空公司的信用悲剧 //那如果我们改用pure virtual函数,并予以实现码呢? //就像这样: class Airplane{ public: virtual void fly(const Airport& destination)=0; ... }; void Airplane::fly(const Airport& destination){ ... }; //那么程序员就必须为新型飞机强制型提供实现函数, //而其实现码便成为了默认的参考版本 class ModelA:public Airplane{ public: virtual void fly(const Airport& destination) { Ariplane::fly(destination); } } class ModelB:public Airplane{ public: virtual void fly(const Airport& destination) { Ariplane::fly(destination); } } class ModelC:public Airplane{ public: virtual void fly(const Airport& destination); } void ModleC::fly(const Airport& destination) { //将C型飞机按新路线开往目的地 } //这样一来,如果程序员不为每个型号的飞机提供fly函数, //编译器便会发出警告,从而避免犯下此类错误,虽然它降低 //一些简洁性,但却增加了一些明确性。另外,由于含有pure //virtual的class只能成为抽象类,不能实例化,因此,它的 //使用场合也被添加了一些限制。
____________________________________________________________________________________________________________________________________
标签:c++ effective c++ 语法优化
原文地址:http://blog.csdn.net/beyond_ray/article/details/43761001