美文网首页C++
《Effective C++》

《Effective C++》

作者: 橙小汁 | 来源:发表于2016-12-13 22:12 被阅读80次

    再读高效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();
    }
    }

    相关文章

      网友评论

        本文标题:《Effective C++》

        本文链接:https://www.haomeiwen.com/subject/koyjmttx.html