《Effective C++》

再读高效c++,颇有收获,现将高效c++中的经典分享如下,希望对你有所帮助。

奉上很火的 你的名字图系列

1、尽量以const \enum\inline 替换#define

(1)对于单纯常量,最好以const\enum替换#define
    #define ASPECT_RATIO 1.653 //大量引用会带来代码体积膨胀
    const double aspectratio=1.653;

    const char * const p = "hello world";//定义一个常量的不变的字符串
    const string p("hello world");//在c++中换string 是更好的 
    //////////////////////////////////////////////////////////////////////
 class Base
{
  private:
         static const int NumTurns;//为了确保此常量仅有一份用static、 为了将常量的作用范围限制于class内
         int scores[NumTurns];
};
const int Base::NumTurns = 5;//定义式

 class Base
 {
    private:
        enum {NumTurns = 5};//防止别人获得一个指针或者引用指向你的整数常量
        int scores[NumTurns];
 }; 
(2)对于形式函数的宏,最好改用inline函数替换
  #define MAX(a,b) ((a)>(b) ? (a):(b))//用宏实现函数导致不必要的运算,以inline替换
 void main()
{
   int a = 5,b = 0;
  cout<<MAX(++a,b)<<endl;//结果是7
  cout<<MAX(++a,b+10)<<endl;//结果是10
  cout<<a<<endl;//结果是8
}

template<typename T>
inline T Max(const T &a,const T &b)
 {
    return a>b ? a:b;
  }
void main()
{
  int a = 5,b = 0;
  cout<<Max(++a,b)<<endl;//结果是6
  cout<<Max(++a,b+10)<<endl;//结果是10
  cout<<a<<endl;//结果是7
}

拓展:

 template<typename T>//inline一般被置于头文件内,因为是在编译阶段完成
 inline const T& max(const T& a,const T& b)//构造和析构往往是Inline的糟糕人选
 {
    return a < b ? b : a;
 }


inline void PrinOrd(BNode *p)//内联inline只是一种建议,底层编译器不一定会给你内联,比如 递归、多层for循环,编译器会自动将inline忽略掉
{
    if(NULL != p)
    {
        PrinOrd(p->left);
        cout<<p->val<<" ";
        PrinOrd(p->right);
    }
}

2、尽可能使用const

(1)可以将函数参数、函数返回类型声明为const,是为了防止修改
class Rational{};
const Rational operator*(const Rational &lhs,const Rational &rhs)//返回值和参数均设为const是为了避免if(a*b = c)的错误以及恶意修改参数的错误
{
    return lhs * rhs;
}
void main()
{
    vector<int> vec;
    const vector<int>::iterator iter = vec.begin();//相当于T* const
    *iter = 10;
    ++iter;//error

    vector<int>::const_iterator citer = vec.begin();//相当于T const*
    *citer = 10;//error
    ++citer;
}

3、了解c++默默编写并调用哪些函数

class TextBlock
{
  public:
TextBlock(char* str):text(str){;}//调用string::string(const char *);构造函数
const char& operator[](size_t index) const
{
    cout << "const" << endl;
    return text[index];
}
char &operator[](size_t index)//返回引用是为了可以改变返回的值,若返回值,是通过eax寄存器带回返回值,是不可以改变返回值的
{
    cout << "non-const" << endl;
    return text[index];
}
void Show(size_t index)//每一个成员函数的默认第一个参数为this指针,如TextBlock *const this
{
    cout<<text[index]<<endl;////////call string []
    //cout<<(*this)[index]<<endl;/////call TextBlock []
}
ostream &operator<<(ostream &out)const//add const==>const TextBlock *const this
{
    out<<text;
    return out;
}
  private:
      string text;
};
ostream &operator<<(ostream &out,const TextBlock& rhs)
{
    rhs<<out;
    return out;
}
void main()
{
    //const char *p = string("hello world");//error
    //const char *p = string("hello world").c_str();//return const char *

    TextBlock tb("hello cc");
    tb.Show(0);/////////Show(&tb,0);//然后 这个&tb(实参)传给了一个形参this , static成员函数、构造函数、友元函数没有this指针
    cout << tb[0] << endl;
    const TextBlock tb2("hello cc");
      cout << tb2[0] << endl;

    TextBlock str("hello cc23");
    char *p = &str[0];
    *p = 'j';
    cout<<str<<endl;
}

4、成员函数后加const,还可以通过在成员变量前加mutable关键字和强转来达到改变成员变量的目的

class CTextBlock
{
public:
    size_t length() const;
    size_t length();
private:
    char *pText;
    mutable size_t textlength;//加mutable关键字可以使得后加const的成员函数可以改变类内的成员变量
    mutable bool lengthIsValid;
};
//再强调一次,成员函数内部后加const和不加const的区别
//CTextBlock  nonconst: this 类型 CTextBlock* cnost this
//CTextBlock  const   : this 类型 const CTextBlock* const this                
size_t CTextBlock::length() const
{
    if(!lengthIsValid)
    {
        textlength = strlen(pText);
        lengthIsValid = true;
    }
    return textlength;
}

5、解决代码重复的方法

  class Base
{
  public:
      const char& operator[](size_t index) const
      {
        //执行边界检查、志记数据访问、检验数据访问性
        return text[index];
    }
    char& operator[](size_t index) //在non-const中调用const函数
{
    //执行边界检查、志记数据访问、检验数据访问性
    //return text[index];
    //return (char&)((const Base*)this)->operator[](index);//c语言
    return const_cast<char&>(static_cast<const Base&>(*this)[index]);//为了防止代码重复度大
}
private:
    string text;
};

6、初始化的两种方式pk——成员初值列和赋值初始化

  class PhoneNumber{};
  class ABEntry
{
  public:
ABEntry(const string& name,const string& address,const list<PhoneNumber>& phones)
    :thename(name),theaddress(address),thephones(phones),numTimes(0){}//thename以name为初值调用拷贝构造函数
//调用成员初值列进行初始化,对于内置类型来说,效率高。
//以其声明的次序被初始化
private:
    string thename;
    string theaddress;
    list<PhoneNumber> thephones;
    int numTimes;
};
ABEntry(const string& name,const string& address,const list<PhoneNumber>& phones)//调用赋值初始化成员变量,效率低
{
    thename = name;
    theaddress = address;
    thephones = phones;
    numTimes = 0;
}

7、c++构造赋值时调用的函数

  class Empty
{
public:
    Empty(){}
    ~Empty(){}
    Empty(const Empty& rhs){}//对象未被声明时使用
    Empty& operator=(const Empty& rhs){}//对象被声明后使用
};
void main()
{
    Empty e1;//构造函数
    Empty e2(e1);//拷贝构造
    e2 = e1;//赋值运算符的重载函数
}

8、阻止复制独一无二的东西

class Uncopyable
{
protected:
    Uncopyable(){}
    ~Uncopyable(){}
private://将拷贝构造和赋值运算符声明为private
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};
class HomeForSale:private Uncopyable{};//通过继承Uncopyable类,阻止复制独一无二的东西

9、异常的相关操作

  class DBconnection
{
  public:
    static DBconnection create();
    void close();
};
  class DBcon//管理DBconnection对象1
{
  public:
    ~DBcon()//处理异常
{
    try{db.close();}//调用abort()处理
    catch(){
        abort();
    }

    try{db.close();}//吞下异常
    catch(){
        ;
    }
}
private:
    DBconnection db;
};

class DBcon//管理DBconnection对象2
{
public:
    void close()//将某个操作函数运行期间抛出的异常交到客户手上
    {
      db.close();
        closed = true;
    }
    ~DBcon()//处理异常
    {
        if(!closed)
        {
          try{db.close();}
          catch(){
            ;
          }
      }
  }
private:
    DBconnection db;
    bool closed;
};

10、抽象类——带有纯虚函数的类

class AWOV//抽象类,供继承使用
{
public:
virtual ~AWOV() = 0;//纯虚函数
};
AWOV::~AWOV(){}//为析构函数提供一份定义

11、不要在构造和析构期间调用virtual函数

class Transaction
{
public:
explict Transaction(const string& logInfo);
void logTransaction(const string& logInfo) const;
};
Transaction::Transaction(const string& logInfo)//不要在构造和析构期间调用virtual函数
{
logTransaction(logInfo);
}

class BuyTransaction:public Transaction
{
public:
BuyTransaction(parameters):Transaction(createLogString(parameters))//派生类构造函数
{
;
}
private:
//目标:如何利用派生类的方法来给基类构造对象?存在以下三种思路
//1.使用虚函数?
//2.使用派生类成员函数?
//3.使用派生类的静态函数
//显然,思路1和2都是依赖于this指针,而构造函数中可以认为对象正在构造,属于半成品,绝对不允许泄露this指针出去
//所以 应该使用思路3
static string createLogString(parameters);//这里放在私有,是为了防止用户在外部乱调用
};

12、令赋值运算符函数返回左值的引用

class Bitmap{};
class Widget//一个协议,为了实现连锁赋值
{
public:
Widget& operator=(const Widget& rhs)//版本一
{
if(this==&rhs)return this;//证同测试,如果是自我赋值,就不做任何事情
delete pb;
pb=new Bitmap(
rhs.pb);
return this;//返回左值的引用
}
Widget& operator=(const Widget& rhs)//版本二,解决版本一new调用或Bitmap构造函数出现异常
{
if(this==&rhs)return this;//证同测试,如果是自我赋值,就不做任何事情
Bitmap
pOrig=pb;
pb=new Bitmap(
rhs.pb);
delete pOrig;
return this;//返回左值的引用
}
void Swap(Widget& rhs);//交换
this和rhs的数据
Widget& operator=(const Widget& rhs)//版本三
{
if(this==&rhs)return *this;//证同测试,如果是自我赋值,就不做任何事情
Widget temp(rhs);
Swap(temp);
return *this;
}
Widget& operator=(Widget rhs)//版本四,提高效率
{
if(this==&rhs)return *this;//证同测试,如果是自我赋值,就不做任何事情
Swap(rhs);
return *this;
}
Widget& operator+=(const Widget& rhs)
{
return *this;//返回左值的引用
}
Widget& operator=(int rhs)
{
return this;//返回左值的引用
}
private:
Bitmap
pb;
};

13、记住对对象内的所有成员变量及基类部分确保复制

void logcall(const string& funcName);//记住对对象内的所有成员变量及基类部分确保复制
class Date{};
class Customer
{
public:
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
private:
string name;
Date lastTransaction;//
};
Customer::Customer(const Customer& rhs):name(rhs.name)
{
logcall("hello cc1");
}
Customer& Customer::operator=(const Customer& rhs)
{
if(this==&rhs) return *this;
logcall("hello cc2");
name = rhs.name;
return *this;
}
class PriorityCustomer:public Customer
{
public:
PriorityCustomer(const PriorityCustomer& rhs);
PriorityCustomer& operator=(const PriorityCustomer& rhs);
private:
int priority;
};
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority)
{
logcall("hello cc1");
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
if(this==&rhs) return *this;
logcall("hello cc2");
Customer::operator=(rhs);
priority=rhs.priority;
return *this;
}

14、管理资源之智能指针

(1)、三种方式解决资源泄露
class Investment{};
Investment* createInvestment();
void f()//版本一,有缺陷
{
Investment* pInv=createInvestment();
//中间执行return或者抛出异常导致无法执行delete,造成资源泄露
delete pInv;
}
void f()//版本二,解决版本一的资源泄露
{
auto_ptr<Investment> pInv(createInvestment());//智能指针,获得资源后立刻放进管理对象内,管理对象运用析构函数确保资源释放
//auto_ptr在复制或者赋值后之前的值变为NULL
}
void f()//版本三,解决版本二的复制赋值错误
{
shared_ptr<Investment> pInv(createInvestment());
}//两个智能指针在其析构函数中做delete而不是delete[]动作,是因为vector和string总是可以取代动态分配而得到的数组
(2)、防止死锁
void lock(Mutex* pm);//锁定pm所指的互斥器
void unlock(Mutex* pm);//将互斥器解除锁定
class Lock:private Uncopyable//版本一,禁止复制
{
public:
explicit Lock(Mutex* pm):mutexPtr(pm)
{
lock(mutexPtr);
}
~Lock()
{
unlock(mutexPtr);
}
private:
Mutex *mutexPtr;
};

class Lock//版本二,对底层资源运用引用计数法
{
public:
explict Lock(Mutex* pm):mutexPtr(pm,unlock)
{
lock(mutexPtr.get());//auto_ptr和shared_ptr都提供一个get成员函数,用来执行显示转换,返回智能指针内部的原始指针
}
~Lock()
{
unlock(mutexPtr);
}
private:
shared_ptr<Mutex> mutexPtr;
};
(3)shared_ptr和auto_ptr
class Investment
{
public:
bool isTaxfree() const;
};
Investment* createInvestment();
shared_ptr<Investment> pi1(createInvestment());
bool taxable1=!(pi1->isTaxfree());//将pi1隐式转换为底部原始指针,并调用operator->取值
auto_ptr<Investment> pi2(createInvestment());
bool taxable2=!((pi2).isTaxfree());//将pi2隐式转换为底部原始指针,并调用operator取值
(4)隐式转换和显示转换函数
Derived getbase();
void releasebase(Derived a);
class base
{
public:
explicit base(Derived a):text(a){}
~base()
{
releasebase(a);
}
Derived get() const{return text;}//显示转换函数
operator Derived() const{return text;}//隐式转换函数

private:
Derived text;
};

(5)以独立语句将newed对象置入智能指针
int priority();
void processWidget(shared_ptr<Widget> pw,int priority);
void main()
{
processWidget(new Widget,priority());//无法通过编译
processWidget(shared_ptr<Widget>(new Widget),priority());//可以通过编译,但执行期间可能造成内存泄漏
shared_ptr<Widget> pw(new Widget);//以独立语句将newed对象置入智能指针#include <memory>//智能指针头文件
processWidget(pw,priority());
}

15、以传引用代替传值

(1)明显的传值pk传引用
class Base{};
class Derived{};
shared_ptr<Base> createbase()
{
return shared_ptr<Base>(new Derived);//解决了cross-Dll problem,shared_ptr会自动使用每一个指针专属的删除器。
}
////////////////////////////////////////////////
class Person
{
public:
Person();
virtual ~Person();//为基类声明虚析构函数
private:
string name;
string address;
};
class student:public Person
{
public:
student();
~student();
private:
string schoolname;
string schooladdress;
};
bool validateStudent(Student s);//需要多次调用构造函数和析构函数,效率低
bool validateStudent(const Student& s);//效率高
void main()
{
Student platu;
bool platuisok = validateStudent(platu);
}
(2)传引用来解决类型切割
class Window
{
public:
string name() const;
virtual void display() const;
};
class WindowWithScrollbars:public Window
{
public:
virtual void display() const;
};
void PrintnameAndDisplay(Window w)//解决类型切割,将Window w改为const Window& w
{
cout<<w.name()<<endl;
w.display();
}
void main()
{
WindowWithScrollbars wsb;
PrintnameAndDisplay(wsb);//此处有类型切割
}
拓展:
//一般STL的迭代器和函数对象以及内置类型都用传值
class Fun
{
public:
void operator()(int x)
{
cout<<x<<endl;
}
};
void main()
{
Fun x;//这就是函数对象
x(123);
}

16、宁可以non-member、non-friend函数,替换member函数

class Rational
{
public:
Rational(int numerator=0,int denomerator=1);
private:
int n,d;
friend const Rational operator* (const Rational& lhs,const Rational& rhs);
};
inline const Rational operator* (const Rational& lhs,const Rational& rhs)//定义为non-member函数是为了支持函数的所有参数进行隐式类型转换,支持混合运算
{
return Rational(lhs.nrhs.n,lhs.drhs.d);//不要返回局部变量(在栈上或者堆上)的引用,也不要返回局部静态变量的引用
}
//将成员变量声明为private可以增加其封装性。
//namespace可以跨越多个源码文件而class不可以。

17、尽可能延后变量定义式的出现时间

string::encryptPassword(const string& password)
{
//string encrypted;
if(paassword.length() < mininumPasswordlength)
{
throw logic_error("password is too short");
}
string encrypted;//延后它的实现,避免构造和析构成本

return encrypted;

}

18、c++的四种转型

const_cast<T>(expression)//将对象的常量性移除
dynamic_cast<T>(expression)//(安全向下转型),用来决定某对象是否归属继承体系中的某一个类型//避免使用
reinterpret_cast<T>(expression)//低级转型,不可移植
static_cast<T>(expression)//强迫隐式转换

class Window//尽量少做转型动作
{
public:
virtual void onResize(){}
};
class SpecialWindow:public Window
{
public:
virtual void onResize()
{
//static_cast<Window>(this).onResize();//这是在当前对象的base_class成分的副本上调用Window::onresize(),然后再当前对象身上执行SpecialWindow专属动作
Window::onResize();
}
void bink();
};
typedef vector<shared_ptr<SpecialWindow>> VPSW;
VPSW winPtrs;
for(VPSW::iterator iter = winPtrs.begin(); iter!=winPtrs.end();++iter)
{
(
iter)->bink();
}
//避免返回handles(包括references,指针,迭代器)指向对象内部,除了operator[]

19、为异常安全而努力是值得的

//=============================版本一===============================
class PrettyMenu
{
public:
void changeBackground(istream& imgSrc);
private:
Mutex mutex;
shared_ptr<Image> bgImage;
int imageChanges;
};
void PrettyMenu::changeBackground(istream& imgSrc)
{
Lock m1(&mutex);//获得互斥器并确保它稍后被释放
//delete bgImage;
//++imageChanges;
//bgImage = new Image(imgSrc);
bgImage.reset(new Image(imgSrc));//reset函数只有在new Image(imgSrc)被成功生成后才调用,delete在reset中被调用
++imageChanges;
}
//======================版本二=======cpoy and swap策略“修改对象的副本”====
struct PMImpl
{
shared_ptr<Image> bgImage;
int imageChanges;
};
class PrettyMenu
{
public:
void changeBackground(istream& imgSrc);
private:
Mutex mutex;
shared_ptr<PMImpl> PImpl;
};
void PrettyMenu::changeBackground(istream& imgSrc)
{
using std::swap;
Lock m1(&mutex);
shared_ptr<PMImpl> pnew(new PMImpl(*PImpl))
pnew->bgImage.reset(new Image(imgSrc));
++pnew->imageChanges;
swap(PImpl,pnew);
}

20、将文件之间的编译依存关系降至最低

//使得编译依存最小化的一般构想是,相依与声明式,不要相依与定义式,基于此构想,有以下
//++=++++++++++++++++++++++++++Handle classes++++++++++++++++++++
//需要两个头文件,一个用于声明式,一个用于定义式
class PersonImpl;//Person的实现类
class Date;
class Address;
class Person
{
public:
Person(const string& name,const Date& birthday,const Address& addr)
:pImpl(new PersonImpl(name,birthday,addr))
{
;
}
string name() const
{
return pImpl->name();
}
string birthdate() const;
string address() const;
private:
shared_ptr<PersonImpl> pImpl;
};

//++=++++++++++++++++++++++++++Interface classes++++++++++++++++++
class Person//抽象类,不可以具现化,必须通过指针或者引用来撰写程序
{
public:
virtual ~Person();
virtual string name() const = 0;
virtual string birthdate() const = 0;
virtual string address() const = 0;
static shared_ptr<Person> Create(const string& name,const Date& birthday,const Address& addr);//工厂函数,返回动态分配所得到的指针
};
class RealPerson:public Person
{
public:
RealPerson(const string& name,const Date& birthday,const Address& addr)
:thename(name),theBirthdate(birthday),theaddress(addr)
{}
virtual ~RealPerson();
string name() const;
string birthdate() const;
string address() const;
private:
string thename;
Date theBirthdate;
Address theaddress;
};
shared_ptr<Person> Person::Create(const string& name,const Date& birthday,const Address& addr)
{
return shared_ptr<Person>(new RealPerson(name,birthday,addr));//会创建不同的derived class对象
}
void main()
{
string name;
Date dateofBirth;
Address address;
shared_ptr<Person> pp(Person::Create(name,dateofBirth,address));
cout<<pp->name<<"was born on"<<pp->birthdate()<<"and now lives at"<<pp->address();
}

21、继承重载函数会被遮掩掉

class Base
{
public:
virtual void mf1() = 0;
virtual void mf1(int a)
{
x=a;
cout<<"mf1:"<<x<<endl;
}
virtual void mf2(int a)
{
x=a;
cout<<"mf2:"<<x<<endl;
}
void mf3(int a)
{
x=a;
cout<<"mf3:"<<x<<endl;
}
void mf4(double a)
{
x=a;
cout<<"mf3:"<<x<<endl;
}
private:
int x;
};
class Derived:public Base//继承重载函数会被遮掩掉
{
public:
///一////////
using Base::mf1;

///二////////
virtual void mf1()
{
    ;
    //Base::mf1();//并不想继承基类多个版本函数
}

};
void main()
{
Derived d;
d.mf1();
d.mf1(5);
d.mf3(9.0);
}

22、区分接口继承和实现继承

class shape
{
public:
virtual void draw()=0;//让继承类只继承接口
virtual void error(const string& name);//让继承类继承接口和缺省实现
int objectid() const;//让继承类继承接口和一份强制性实现
};
class Rectangle:public shape
{};

23、函数指针举例

class Person;
class Health
{
public:
typedef int (Hander)( Person&);
Health(Hander f):mfun(f){;}
int operator()(Person& src){return mfun(src);}
void Change(Hander f ){mfun = f;}
private:
Hander mfun;
};
int default_fun(Person& x)
{
return 10000;
}
int fun1(Person& x)
{
return 100;
}
class Person:public Health
{
public:
Person(int x,Hander f = default_fun):data(x),Health(f){;}
void ShowHealth()
{
cout<<Health::operator ()(
this)<<endl;
}
void Change(Hander f = default_fun){Health::Change(f);}
private:
int data;
};
void main()
{
Person a(1);
a.ShowHealth();//10000
a.Change(fun1);//会覆盖掉Person中change成员函数的参数默认值
a.ShowHealth();
}

24、考虑virtual函数以外的其它选择

(1)、template method模式
class Gamecharacter
{
public:
int HealthValue() const//NVI手法,优点是可以在调用一个virtual函数之前设定好一些场景
{
int retval = doHealthValue();
return retval;
}
private:
virtual int doHealthValue() const
{
}
};
(2)、Strategy模式
///////////////////一、基于Function Pointers的Strategy模式/////////////////////////
class Gamecharacter;
int defaultHealthCalc(const Gamecharacter& gc);
class Gamecharacter
{
public:
typedef int (HealthCalcFunc)(const Gamecharacter& gc);
explicit Gamecharacter(HealthCalcFunc hcf=defaultHealthCalc)
:HealthFunc(hcf)
{
;
}
int healthValue() const
{
return HealthFunc(
this);
}
private:
HealthCalcFunc HealthFunc;
};

///////////////////二、基于tr1::Function的Strategy模式/////////////////////////
class Gamecharacter;
int defaultHealthCalc(const Gamecharacter& gc);
class Gamecharacter
{
public:
typedef tr1::function<int (const Gamecharacter&)> HealthCalcFunc;
//函数接收一个引用指向const Gamecharacter,并返回int。这个tr1::function类型产生的对象可以持有任何与此签名式兼容的可调用物,
//也就是说这个可调用物的参数可以被隐式转换为const Gamecharacter,而且其返回类型可以被隐式转换为int
explicit Gamecharacter(HealthCalcFunc hcf=defaultHealthCalc)
:HealthFunc(hcf)
{
;
}
int healthValue() const
{
return HealthFunc(this);
}
private:
HealthCalcFunc HealthFunc;
};
///////////////////三、古典的Strategy模式/////////////////////////
class Gamecharacter;
class HealthCalcFunc
{
public:
virtual int calc(const Gamecharacter& gc) const
{}
};
HealthCalcFunc defaultHealthCalc;
class Gamecharacter
{
public:
explicit Gamecharacter(HealthCalcFunc
phcf = &defaultHealthCalc)
:pHealthCalc(phcf)
{}
int healthValue() const
{
return pHealthCalc->calc(this);
}
private:
HealthCalcFunc
pHealthCalc;
};

25、绝不重新定义继承而来的non-virtual函数和继承而来的缺省参数值

//绝对不重新定义继承而来的non_virtual函数,因为它是静态绑定
//绝对不重新定义继承而来的缺省参数值,因为它是静态绑定
class Shape
{
public:
enum ShapeColor{Red,Green,Blue};//默认第一个值为int类型的0
virtual void Draw(ShapeColor color = Red) const = 0
{
cout<<"Shape::Draw "<<color<<endl;
}
};
class Rectangle:public Shape
{
public:
virtual void Draw(ShapeColor color = Green) const//缺省参数是静态绑定,重新定义没有什么卵用
{

    cout<<"Rectangle::Draw "<<color<<endl;
}

};
class Circle:public Shape
{
public:
virtual void Draw(ShapeColor color) const//指针调用此函数时,不需要指定参数值;对象调用此函数时,需要指定参数值
{

    cout<<"Circle::Draw "<<color<<endl;
}

};

void main()
{
Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;
pc->Draw();//Circle::Draw 0
pr->Draw();//Rectangle::Draw 0
Circle cc;
Shape::ShapeColor color = Shape::Green;/////Green代表是在全局下寻找Green这个名字,而不是在Shape这个类作用域下寻找
cc.Draw(color);//Circle::Draw 1
}
/////======================================/////
/////////////////////////////////////版本1 代码

class Shape
{
public:
enum ShapeColor{Red,Green,Blue};//枚举
void Draw(ShapeColor color = Red)
{
cout<<"Shape::Draw "<<color<<endl;
}
};
class Rectangle:public Shape
{
public:
void Draw(ShapeColor color = Green)
{

    cout<<"Rectangle::Draw "<<color<<endl;
}

};

void main()
{
Shape s;
s.Draw();//Shape::Draw 0
Rectangle r;
r.Draw();//静态绑定,默认参数没有从基类中获取//Rectangle::Draw 1
}

////////////////////////////////////版本2 代码

class Shape
{
public:
enum ShapeColor{Red,Green,Blue};
virtual void Draw(ShapeColor color = Red)
{
cout<<"Shape::Draw "<<color<<endl;
}
};
class Rectangle:public Shape
{
public:
virtual void Draw(ShapeColor color = Green)//重新定义继承而来的缺省参数值,并没有卵用
{

    cout<<"Rectangle::Draw "<<color<<endl;
}

};

void main()
{

Shape* pc = new Shape;
Shape* pr = new Rectangle;
pc->Draw();//Circle::Draw 0
pr->Draw();//Rectangle::Draw 0

}

////////////////////////////////////版本3代码
class Shape
{
public:
enum ShapeColor{Red,Green,Blue};
void Show(ShapeColor color = Red)
{
cout<<"S SHOW"<<endl;
Draw(color);
}
private:
virtual void Draw(ShapeColor color = Red)
{
cout<<"Shape::Draw "<<color<<endl;
}
};
class Rectangle:public Shape
{
public:
void Show(ShapeColor color = Green)
{
cout<<"R SHOW"<<endl;
Draw(color);
}
private:
virtual void Draw(ShapeColor color = Green)
{

    cout<<"Rectangle::Draw "<<color<<endl;
}

};

void main()
{

Shape* pc = new Shape;
Shape* pr = new Rectangle;
pc->Show();//S SHOW Shape::Draw 0
pr->Show();//S SHOW Rectangle::Draw 0
Shape s;
Rectangle r;
s.Show();//S SHOW Shape::Draw 0
r.Show();//R SHOW Rectangle::Draw 1

}

26、用list来实现set

////////////用list来实现set,但list允许存在重复元素,set不可以内含重复元素
template<typename T>
class Set
{
public:
bool member(const T& item) const;
void insert(const T& item);
void remove(const T& item);
size_t size() const;
private:
list<T> rep;
};
template<typename T>
bool Set::member(const T& item) const
{
return std::find(rep.begin(),rep.end(),item) != rep.end();
}
template<typename T>
void Set::insert(const T& item)
{
if(!member(item))
rep.push_back(item);
}
template<typename T>
void Set::remove(const T& item)
{
typename list<T>::iterator it = std::find(rep.begin(),rep.end(),item);
if(it!=rep.end())
rep.erase(it);
}
template<typename T>
size_t size() const
{
return rep.size();
}

27、private继承可以造成Empty base最优化

class Empty{};
class Base1
{
public:
private:
int x;
Empty y;
};

class Base2:private Empty
private:
int x;
};
void main()
{
cout << sizeof(Base1) <<" "<< sizeof(Base2)<< endl;//8 4
}

28、模板

(1)、模板规则
//对class 而言接口是显式的,多态则是通过虚函数发生于运行期
//对template而言接口是隐式的,多态则是通过template具现化和函数重载解析发生于编译期。
template<class T>//class和typename可以互换
class Base{};
template<typename T>
class Derived{};

template<typename T>
class Derived:public Base<T>::Nested//不允许在基类列typename
{
public:
explicit Derived(int x)
:Base<T>::Nested(x)//不允许在成员初值列加上typename
{
typename Base<T>::Nested temp;//在嵌套从属类型名称的前面需要加上typename
}
};

template<typename IterT>
void WorkWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;//若IterT是vector<int>::iterator,则temp的类型就是int
value_type temp(*iter);
}

template<class T>//声明成员模板用于泛化copy构造和泛化assignment操作,还是需要声明正常的copy构造函数和assignment操作符
class shared_ptr
{
public:
shared_ptr(shared_ptr const& r);

template<class Y>
shared_ptr(shared_ptr<Y> const& r);

shared_ptr& operator=(shared_ptr const& r);

template<class Y>
shared_ptr& operator=(shared_ptr<Y> const& r);

};

//当我们编写一个类模板,而它所提供的与此模板相关的函数支持所有参数之隐式类型转换,请将那些函数定义为类模板内部的friend函数
template<typename T>
class Rational
{
public:
Rational(const T& numerator=0,
const T& denominator=1);
const T numerator() const;
const T denominator() const;
//将函数写在类内部是因为让这个函数自动具现化
friend const Rational operator* (const Rational& lhs,const Rational& rhs)//声明为friend是因为在类内部声明non-member函数唯一方法是令它成为一个friend
{
return Rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());
}

};
//为了让类型转换发生在所有实参身上,所以定义non-member函数
template<typename T>
const Rational<T> operator* (const Rational<T>& lhs,const Rational<T>& rhs)
{}

(2)、模板特化
class Companya
{
public:
void sendClearText(const string& msg)
{
cout<<"Companya::sendClearText"<<endl;
}
void sendEncrypted(const string& msg)
{
cout<<"Companya::sendEncrypted"<<endl;
}
};

class Companyb
{
public:
void sendClearText(const string& msg)
{
cout<<"Companyb::sendClearText"<<endl;
}
void sendEncrypted(const string& msg)
{
cout<<"Companyb::sendEncrypted"<<endl;
}
};

class MsgInfo{};//用来保存信息,以备将来产生信息

template<typename Company>
class MsgSender
{
public:
void sendClear(const MsgInfo& info)
{
string msg;
Company c;
c.sendClearText(msg);
}
void sendSecret(const MsgInfo& info)
{}
};

class Companyz
{
public:
void sendEncrypted(const string& msg);
};

template<>//一个特化版的MsgSender template,在template实参是Companyz时被使用,这是模板全特化
class MsgSender<Companyz>
{
public:
void sendSecret(const MsgInfo& info)
{}
};

template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
void sendClearMsg(const MsgInfo& info)
{
sendClear(info);//调用基类的函数,无法通过编译
//this->sendClear(info);
}
};
void main()
{
LoggingMsgSender<Companya> aMsgSender;
MsgInfo msgData;
aMsgSender.sendClearMsg(msgData);
LoggingMsgSender<Companyz> zMsgSender;
//zMsgSender.sendClearMsg(msgData);//无法通过编译,因为特化版的MsgSender在实参是Companyz时没有sendClear函数
}
(3)、模板造成的代码膨胀及解决方法
template<typename T,size_t n>
class SquareMatrix
{
public:
void invert();
};
void main()
{
SquareMatrix<double,5> sm1;//具现化两份代码,造成代码膨胀
sm1.invert();
SquareMatrix<double,10> sm2;
sm2.invert();
}
//因非类型参数而造成的代码膨胀,采用以函数参数或class成员变量替换template参数来达到消除
//因类型参数而造成的代码膨胀,让带有完全相同的二进制表述的具现类型共享实现码
template<typename T>
class SquareMatrixBase
{
protected:
void invert(size_t matrixSize);
};
template<typename T,size_t n>
class SquareMatrix:private SquareMatrixBase<T>
{
private:
using SquareMatrixBase<T>::invert;
public:
void invert()
{
this->invert(n);
}
};

29、元编程计算阶乘

template<unsigned n>
struct Factorial
{
enum{value = n*Factorial<n-1>::value};
};

template<>
struct Factorial<0>
{
enum{value = 1};
};

30、理解bad_alloc

class A
{
public:
A(int x):data(x){;}
void Show(){cout<<data<<endl;}
private:
int data;
};
//bad_alloc就像A一样 是一个类
void fun(int x)throw(A)
{
if(x > 10) throw A(12);

}
void main()
{
try
{
fun(12);
}
catch(A &ra)
{
cout<<"hello"<<endl;//输出hello
}
}

31、理解new

(1)、set_new_handler
void outofmem()
{
cerr<<"unable to satisfy request for memory\n";
abort();
}
void main()
{
set_new_handler(outofmem);//指定new失败时,调用的函数
//int *p = new int[10000000000000];////有一些编译器里面 如果new 一个特别特别大的数字的话,那么编译器会这样处理new int[0],即申请0字节
int *p = new int[0x1234];
delete []p;
}
(2)、new的底层实现

typedef void (new_handler) ()
void
operator new(size_t size) throw(bad_alloc)//这是函数列表的一部分,表示不抛出异常 ,如果要抛出异常则参考
{
if(size == 0)
size = 1;
while(true)
{
//分配内存;
if(分配成功)
return 指针;
new_handler globalHandler=set_new_handler(0);
set_new_handler(globalHandler);
if(globalHandler)
(*globalHandler)();
else throw bad_alloc();
}
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容