标签: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