1、抽象类

  如果一个类里面有纯虚函数,其被编译器认为是一个抽象类,抽象类不能用来实例化一个对象

  纯虚函数定义:virtual void 函数名(void)=  0;

  抽象类是给派生类定义好接口函数,如果派生类不实现抽象类中的所有纯虚函数,这个派生类还是一个抽象类,不能实例化对象

2、抽象类界面

  一个程序由多个人编写,分为:

  应用编写:使用类

  类编写:提供类(把提供类编译成库)

  eg:Makefile内容

  Human:main.o libHuman,so

    g++ -o $@ $< -L./ -lHuman

  %.o:%.cpp

    g++ -fPIC -c -o $@ $<

  libHuman.so:Englishman.o Chinese.o Human.o

    g++ -shared  -o $@ $^

  make后执行命令:LD_LIBRARY_PATH=./ ./Human

  这个时候如果修改Englishman.cpp ,make libHuman.so的时候只会重新生成libHuman.so,不会重新生成应用程序

  对上面那个Makefile,如果.h和.cpp都发送了变化,这个时候如果仅make  libHuman.so,运行的时候程序会崩溃,怎么样解决这种问题?

  解决办法是main.c中仅包含一个头文件a.h,main中需要实例化某个对象的时候让该类提供一个函数,这个函数在a.h中声明,并在类中实现:

  eg:在类中Human & CreateEnglishman(char *name,int age,char *address){ return *(new Englishman(name,age,address))};

    在a.h中Human & CreateEnglishman(char *name,int age,char *address);//注意a.h是中间头问题,被main.c包含,a.h也被类所在的cpp包含

  注意:通过delete 来删除CreateEnglishman返回的对象的时候,是调用huamn的析构函数,所有需要把析构函数声明为虚函数,这样能根据对象的实际情况调用相印的类的析构函数,析构函数不能是纯析构函数,否则在派生类必须要实现同名的析构函数,否则派生类还是抽象类(有纯虚函数的类都是抽象类)

3、函数模板

template<typename T>

T& mymax(T& a,T& b)

{

  return (a < b)?b:a;

}

使用的方法:int ia =1,ib =2;mymax(ia,ib);

函数模块只是编译指令,一般写在头文件中,编译程序的时候,编译器根据函数的参数来“推导”模板的参数,然后生成具体的函数

比如上面的使用方法会生成如下代码:

int& mymax(int& a,int& b)

{

  return (a < b)?b:a;

}

函数模版支持两中隐式转换(比如上述mymax函数模版,当传入的a和b的类型不一致时,编译器会报错推导不出函数,仅两张情况例外):

A、const转换,模块参数声明为const:T& mymax(const T&,const T&b);这个时候传入的参数可以不带const属性;

B、数组或函数指针转换:T& mymax(T& a,T& b);如果char a[]="abc";char b="cd";mymax(a,b)会出错,因为a,b在这里实际上是(char[4],char[3]),无法推导出T

如果存在多个可匹配的模块函数和普通函数

eg:template<typename T>

const T& mymax(const T& a,const T& b)

{

  return (a<b)?b:a;

}

const T& mymax(T& a,T& b)

{

  return (a<b)?b:a;

}

const int& mymax(const int& a,const int& b)

{

  return (a<b)?b:a;

}

int main(int argc,char **argv)

{

  int ia =1;int ib =2;

  mymax(ia,ib);

}

函数选择过程:

A、列出所有匹配的函数:

  第一个模板函数推导后的函数:mymax(const int& a,const int& b)

  第二个模板函数推导后的函数:mymax(int& a,int& b)

  第三个普通函数:mymax(const int& a,const int& b)

B、根据参数的转换排序:第二个模块和第三个函数排在第一位;第一个模板排在第二位

  第一个模板int->const int

  第二个模板int->int

  第三个函数int->int

C、如果某个候选函数的参数跟调用时传入的参数跟匹配,则选择此候选函数

D、如果这些候选函数的参数匹配度相同

  首先,优先选择普通函数

  其次,对于多个模板函数,选择“更特化”的(更特化指的是参数更特殊、更具体、更细化)

  否则出现二义性错误

4、类模块

template<typename T>

class AAA{

private:

  T t;

public:

  void test_func(const T &t);

  void print(void);

}

template<typename T> void AAA<T>::test_func(const T &t)

{

  this->t = t;

  count<<t<<endl;

}

template<typename T> void AAA<T>::print(void)

{

  cout<<t<<endl;

}

int main(int argc,char **argv)

{

  AAA<int> a;

  a.test_func(1);

  a.print();

  AAA<double> b;

  b.test_func(1.23);

  b.print();

  return 0;

}

同时如果对当前的类模块不满意,也可以定做上面的类模版

在上面的基础上添加代码:

template<>

class AAA<int>{

public:

  void test_func_int(const int & t)

  {

    cout<<t<<endl;

  }

  void print_int(void);

}

void AAA<int>::print_int(void)

{

  cout<<"for test"<<endl;

}

重写main函数:

int main(int argc,char **argv)

{

  AAA<int> a;

  a.test_func_int(1);

  a.print_int();

  AAA<double> b;

  b.test_func(1.23);

  b.print();

  return 0;

}

5、异常

三个函数A、B、C,其中A调用B,B调用C

C()

{

  return XX;

}

B()

{

  if(C()){}

  else return -Err;

}

A()

{

  if(B()){}

  else {}

}

如果C出错了,A需要去处理这个错误,就需要一级一级的判断,这样如果存在N级调用的时候会非常麻烦,在C++中通过异常来处理该问题

函数A捕获函数C发出的异常,举例如下:

void C(){

  throw 1;//抛出异常后直接跳到A中的catch处执行了,如果这里抛出的不是int类型的数据,程序会崩溃

}

void B(){

  cout<<"call c...."<<endl;

  C();

  cout<<"After call c...."<<endl;//当在C中抛出异常后,这条语句不会在执行

  }

void A(){

  try{

    B{}

  }catch (int i)

  {

    cout<<"catch exception "<<i<<endl

  }

  //catch (...){}//这里的省略号表示所有异常

}

当抛出了个派生类的时候,如果catch(基类),会发生隐式转换,catch能捕获异常。

可以在函数后面声明这个函数会抛出那些异常,如果其throw非声明异常,程序会崩溃,不管有没相印的catch:eg :void C() throw(int,double){}

抛出异常如果函数没处理,其实际上是交给系统来处理了,会执行2个函数“unexpected”和“terminate”,unexpected函数用来处理声明之外的异常,terminate函数用来处理catch分支未捕获到的异常,如果都没有会两者都调用

可以通过下面语句修改这个默认的处理函数

eg:set_unexpected(异常处理函数);

也可以设置终止函数:

eg:set_terminate(终止函数名称);

6、智能指针(下面的sp就是只能指针)

void test_func(void) {

  Person *p = new Person();

}

int main(int argc,char **argv)

{

  test_func();//没有释放new的Person对象,会造成内存泄漏,知道main函数退出

}

如果test_func改为{Person per;}//局部变量,函数退出时对象就会被释放,调用其析构函数

使用指针也能不会造成内存泄漏的方法:

方法1:

class sp{

private:

  Person *p;

public:

  sp(Person *other)

  {

    p =other;

  }

  ~sp()

  {

    if(p)

      delete p;

  }

}

void test_func(void) {

  sp s = new Person();

}

int main(int argc,char **argv)

{

  test_func();

}

对方法一进行修改:

class sp{

private:

  Person *p;

public:

  sp():p(0){}//构造函数p的默认值是0

  sp(Person *other)

  {

    p =other;

  }

  ~sp()

  {

    if(p)

      delete p;

  }

  Person *operator->()//重载->

  {

    return p;

  }

}

void test_func(void) {

  sp s = new Person();

  s->printInfo();//调用的是Person里面的函数,因为“->”重载了

}

int main(int argc,char **argv)

{

  int i;  

  for(i =0;i<2;i++)

    test_func();

}

再次修改:

class sp{

private:

  Person *p;

public:

  sp():p(0){}

  sp(Person *other)

  {

    p =other;

  }

  sp(sp &other)

  {

    p =other.p;

  }

  ~sp()

  {

    if(p)

      delete p;

  }

  Person *operator->()//重载->

  {

    return p;

  }

}

void test_func(sp &other) {

  sp s = other;

  s->printInfo();//调用的是Person里面的函数,因为“->”重载了

}

int main(int argc,char **argv)

{

  int i;  

  sp other = new Person()//编译会出错

/*

  上面这句代码相当与:

  A、Person *p = new Person()

  B、sp tmp(p);相当于调用sp(Person *other)构造函数

  C、两种可能:1、sp other(tmp);相当于调用sp(sp &other)构造函数

          出问题的愿意是sp & other这个引用来指向tmp这个临时变量,这是不对的,引用没法执行一个临时变量;但是const sp &other可以指向tmp,所以修改sp(sp &other)构造函数为sp(const sp &other)

          2、tmp转换成Person*,在调用sp(Person *other)构造函数来生成other

          tmp是一个sp对象,其没办法隐式转换成Person *指针

*/

   test_func(other );  

}

执行结果正确;

但是如果main函数改为下面,程序运行会崩溃:

int main(int argc,char **argv)

{

  int i;  

  sp other = new Person();

  for(i=0;i<2;i++)

   test_func(other );  //原因是第一次退出test_func的时候会释放掉new分配的Person空间,第二次循环的时候在次调用s->printInfo();时候,Person对象已经不存在了,可以通过在Person对象中加一个引用计数,并添加获取、加1、减1三个函数,同时在sp类中所有的构造函数都调用加1函数,析构函数中先调用减1函数,接着调用获取函数来得到引用计数的值,如果为零,就调用delete p来释放,并把p设置为NULL。

}

同时还可以在sp中重载“*”

eg:Person& operator*()

  {

    return *p;

  }

在main中

  sp other = new Person();

  (*other).printlnfo();

可以把sp定义为类模块:

template<typename T>

class sp{

private:

  T *p;

public:

  sp():p(0){}

  sp(T *other)

  {

    p = other;

    p->incStrong();

  }

  sp(const sp &other)

  {

    p = other.p;

    p->incStrong();

  }

  ~sp()

  {

    if(p)

    {

      p->decStrong();

      if(p->getStrongCount() == 0)

      {

        delete p;

        p = NULL;

      }

    }

  }

  T *operator->()

  {

    return p;

  }

  T& operator*()

  {

    return *p;

  }

}

template<typename T>

void test_FUNC(SP<T> &other)

{

  sp<T> s =other;

  s->printInfo();

}

int main(int argc,char **argv)

{

  sp<Person> other = new Person();

  test_func(other);

}

7、Android轻量级指针(在源码RefBase.h中,sp的相关源码在StrongPointer.h中)

  对上面的代码,引用计数的加1和减1操作不是原子的,在多线程的时候存在风险,在Android源码的RefBase.h中,其也是维护了一个引用计数,其加1和减1如下:

  void incStrong()

  {

    __sync_fech_and_add(&mCount,1);

  }

  void decStrong()

  {

    //__sync_fetch_and_sub(&mCount,1)返回的是减一之前的指

    if(__sync_fetch_and_sub(&mCount,1)==1){

      delete static_case<const T*>(this);

    }

  }

  修改6中的代码继承源码中的智能指针:

using namespace std;

using namespace android::RSC;

class Person : public LightRefBase<Person>{//LightRefBase里面有sp的相关代码

public:

  Person(){

    cout<<"Person()"<<endl;

  }

  ~Person()

  {

    cout<<"~Person()"<<endl;

  }

  void printlnfo(void)

  {

    cout<<"just a test function"<<endl;

  }

}

template<typename T>

void test_FUNC(sp<T> &other)

{

  sp<T> s =other;

  s->printInfo();

}

int main(int argc,char **argv)

{

  sp<Person> other = new Person();

  test_func(other);

}

8、弱指针的引人

  智能指针的缺陷

  eg:test_func()

  {

    sp<Person> a = new Person(); //a指向的对象的count = 1

    sp<Person> b = new Person();//b指向的对象的count = 1

    a->setFather(b);//a引用了b;b所指向的count =2;

    b->setSon(a);//b引用了a;a所指向的count =2;

  }

  test_func程序退出的时候~a和~b执行,两者的count =1,这个时候会产生内存泄漏

  

  eg:

using namespace std;

using namespace android::RSC;

class Person : public LightRefBase<Person>{//LightRefBase里面有sp的相关代码

private:

  sp<Person> father;

  sp<Person> son;

public:

  Person(){

    cout<<"Person()"<<endl;

  }

  ~Person()

  {

    cout<<"~Person()"<<endl;

  }

  void setFather(sp<Person> &father)

  {

    this->father = father;

  }

  void setSon(sp<Person> &son)

  {

    this->son= son;

  }

  void printlnfo(void)

  {

    cout<<"just a test function"<<endl;

  }

}

void test_FUNC()

{

    sp<Person> father = new Person();

    sp<Person> son = new Person();

    father ->setSon(son );

    son ->setFather(father );

}

int main(int argc,char **argv)

{

  test_func(other);

  return 0;

}

执行结果:

Person()

Person()

如果去掉son ->setFather(father );

执行结果:

Person()

Person()

~Person()

~Person()

分析原因:如果对象里含有其他对象成员,构造时先构造其他对象成员,在构造对象本事;析构时顺序正好相反

sp<Person> father = new Person();

1、对于new Person()

1.1 Person对象里面的father被被构造,执行sp的默认构造函数inline sp():m_ptr(0){},什么都没做

1.2 Person对象里面的son被构造,执行sp的默认构造函数inline sp():m_ptr(0){},什么都没做

1.3 Person对象本身被构造

2、Person对象的指针传给“sp<Person> father”

  导致sp(T*other)被调用

  它增加了这个Person对象的引用计数(现在此值等于1)

sp<Person> son= new Person();

1、对于new Person()

1.1 Person对象里面的father被被构造,执行sp的默认构造函数inline sp():m_ptr(0){},什么都没做

1.2 Person对象里面的son被构造,执行sp的默认构造函数inline sp():m_ptr(0){},什么都没做

1.3 Person对象本身被构造

2、Person对象的指针传给“sp<Person> son”

  导致sp(T*other)被调用

  它增加了这个Person对象的引用计数(现在此值等于1)

1、setSon里面执行的是"="操作,sp的“=”符号被重载了,它会再次增加son对象的引用计数变成2了

father ->setSon(son );

1、setFather里面执行的是"="操作,sp的“=”符号被重载了,它会再次增加father对象的引用计数变成2了

son ->setFather(father );

当test_func被执行完后,father和son被析构

1、father析构的时候调用~sp():会调用对象的decStrong函数来减1引用计数,还没为0,不会delete掉对象

1、son析构的时候调用~sp():会调用对象的decStrong函数来减1引用计数,还没为0,不会delete掉对象

  对于father指向的对象,其通过father ->setSon(son )引用了son对象,导致son计数变成2了,其可以决定son指向对象的生死,同理son指向的对象,其通过son->setFather(father)引用了father对象,导致father计数变成2了,其可以决定son指向对象的生死

  如果想杀掉father,需要先杀掉son中的引用值,同理杀掉son,需要先杀掉father中的引用值,这样就死锁了。

  强指针/强引用   A指向B,A决定B的生死

  弱指针/弱引用  A指向B,A不能决定B的生死

  本例子这里是强引用,导致死锁,必须引入弱引用或者弱指针,使得father ->setSon(son )时不增加引用计数就可以解决该问题。

9、强弱引用(强弱指针)的引入

 在RefBase基类中有定义了两个引用计数,分别用于强弱引用

 强引用的使用sp<Person>,sp的构造函数肯定是调用incStrong来增加强引用计数,析构函数调用decStrong来减少引用计数

弱引用的使用wp<Person>,wp的构造函数肯定是调用incWeak来增加强引用计数,析构函数调用decWeak来减少引用计数

 eg:本例子需要复制Android源码中的RefBase.cpp到本工程中,还有一些头文件

 举例person9.cpp

  Makefile:

  person9:person9.o RefBase.o

    g++ -o $@ $^

  %.o : %.cpp

    g++ -c -o $@ $< -I include

  clean:

    rm -f *.o person9

  person9.cpp

using namespace std;

using namespace android;

class Person : public RefBase{

private:

  wp<Person> father;

  wp<Person> son;

public:

  Person(){

    cout<<"Person()"<<endl;

  }

  ~Person()

  {

    cout<<"~Person()"<<endl;

  }

  void setFather(sp<Person> &father)

  {

    this->father = father;

  }

  void setSon(sp<Person> &son)

  {

    this->son= son;

  }

  void printlnfo(void)

  {

    cout<<"just a test function"<<endl;

  }

}

void test_FUNC()

{

    sp<Person> father = new Person();

    sp<Person> son = new Person();

    father ->setSon(son );

    son ->setFather(father );

}

int main(int argc,char **argv)

{

  test_func(other);

  return 0;

}

执行结果:

Person()

Person()

~Person()

~Person()

解决了强指针相互引用导致死锁的问题

eg:

修改上面的main函数:

int main(int argc,char **argv)

{

  wp<Person> s = new Person();

  s->printlnfo();//编译的时候会出错,因为弱指针里面“->”没有重载,也没有重载“*”,因为wp进简单引用,没有控制对象的生死,所以如果重载"->"或者“*”,在使用的时候可能对象已经释放了,存在风险,所以源码中没有重载

  return 0;

}

所以要改为强指针

int main(int argc,char **argv)

{

  sp<Person> s = new Person();

  if(s != 0)

    s->printlnfo();

  return 0;

}

eg:修改person.cpp

using namespace std;

using namespace android;

class Person : public RefBase{

private:

  char *name

  wp<Person> father;

  wp<Person> son;

public:

  Person(){

    cout<<"Person()"<<endl;

  }

  Person(char *name){

    cout<<"Person(char *name)"<<endl;

    this->name = name;

  }

  void setFather(sp<Person> &father)

  {

    this->father = father;

  }

  void setSon(sp<Person> &son)

  {

    this->son= son;

  }

  ~Person()

  {

    cout<<"~Person()"<<endl;

  }

  char *getNmae(void)

  {

    return name;

  }

  void printlnfo(void)

  {

    sp<Person> f = father.promote();//弱指针转换成强指针

    sp<Person> s = son.promote();

    cout<<"I am"<<endl;

    if(f  != 0)

      cout<<"My father is "<<f->getName()<<endl;

    if(s != 0)

      cout<<"My son is "<<s->getName<<endl;

  }

}

void test_FUNC()

{

    sp<Person> father = new Person("LiYiShi");

    sp<Person> son = new Person("LiErShi");

    father ->setSon(son );

    son ->setFather(father );

    father->printInfo();

    son ->printInfo();

}

int main(int argc,char **argv)

{

  test_func();

  return 0;

}

执行结果:

Person(char*name)

Person(char*name)

I am LiYiShi My son is LiErShi

I am LiErShi My fatheris LiYiShi

~Person()

~Person()

本例子使用弱指针解决了相互引用导致的问题,并且将弱指针转换为强指针可以引用对象中的函数。

强引用计数永远小与弱引用计数

10、单例模式

最简单的单例模式使用一个全局指针,如果指针为空,就new一个对象,否则直接使用

eg:

class Singleton;

Singleton *gInstance;

class Singleton{

public:

  static Singleton *getInstance()

  {

    if(NULL = gInstance)

      gInstance = new Singleton;

    return gInstance;

  }

  Singleton()

  {

    cout<<"singleton()"<<endl;

  }

  void printInfo(){cout<<"This is singleton"<<endl}

};

int main(int argc,char ** argv)

{

  Singleton *s = Singleton::getInstance();

  s->printInfo();

  Singleton *s = Singleton::getInstance();

  s->printInfo();

  Singleton *s = Singleton::getInstance();

  s->printInfo();

  return 0;

}

执行结果:

Singleton()//只会执行一次构造函数

This is singleton

This is singleton

This is singleton

修改main函数:

void *start_routine_thread1(void *arg)

{

  cout<<"this is thread 1...."<<endl

  Singleton *s = Singleton::getInstance();

  s->printInfo();

  return NULL;

}

void *start_routine_thread2(void *arg)

{

  cout<<"this is thread 2...."<<endl

  Singleton *s = Singleton::getInstance();

  s->printInfo();

  return NULL;

}

int main(int argc,char ** argv)

{

  //创建线程,在线程里也去调用Singleton::getInstance()

  pthread_t thread1ID;

  pthread_t thread2ID;

  pthread_create(&thread1ID,NULL,start_routine_thread1,NULL);

  pthread_create(&thread2ID,NULL,start_routine_thread2,NULL);

  sleep();

  return 0;

}

上面的例子还是存在风险,如果两个线程同时执行,存在几率调用两次构造函数,改进方法是给那段代码加上锁;

eg:

 static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZE;

 class Singleton{

  static Singleton *getInstance()

  {  

    if(NULL = gInstance)

    {

      pthread_mutex_lock(&g_tMutex );

      if(NULL = gInstance)      

        gInstance = new Singleton;

      pthread_mutex_lock(&g_tMutex );
    }

    return gInstance;

  }

}

同时如果有人不使用getInstance()来实例化对象,而是调用Singleton *s = new Singleton();甚至Singleton s5,这些都会调用构造函数

解决办法:把构造函数声明为private来解决

优化:

把全局变量gInstance和锁移到类内部,并声明为static

eg:

 class Singleton{

 private:

    static Singleton *gInstance;

    static pthread_mutex_t g_tMutex ;

 public:

  static Singleton * getInstance()

  {。。。。}

  。。。。

}

static在类外初始化:

Singleton *Singleton ::gInstance;

pthread_mutex_t Singleton ::g_tMutex = PTHREAD_MUTEX_INITIALIZE;

上面这种单例设计模式被称为懒汉模式(用到时才生存实例对象),对应的还有饿汉模式

饿汉模式就是在类外初始化的时候直接实例化,这个时候也不需要锁了:

Singleton *Singleton ::gInstance = new Singleton();

11、桥接模式(android源码中只要发现了impl,其肯定就是使用桥接模式)

作用:将抽象部分和它的实现部分分离,使他们都可以独立地变化

说通俗点就是在一个类里面放另一个类的指针,这个指针指向令一个对象,使用这个指针来访问对象中的函数

举例:给电脑安装操作系统

eg:using namespace std;

class OS{

public :

  virtual void install() = 0;//纯虚函数

}

class LinuxOS : public OS{

public:

  virtual void Install(){cout<<"Install Linux OS"<<endl;}

}

class WindowsOS : public OS{

public:

  virtual void Install(){cout<<"Install Windows OS"<<endl;}

}

class Computer{

public:

  virtual void printInfo() = 0;

}

class MAC:public Computer{

public:

  virtual void printInfo(){cout<<"This isMac"<<endl;};

}

class MacWithLinux: public LinuxOS,public MAC {

public:

  void InstallOS(){printInfo();Install();}

}

class MacWithWindows: public WindowsOS,public MAC {

public:

  void InstallOS(){printInfo();Install();}

}

class Lenovo:public Computer{

public:

  virtual void printInfo(){cout<<"This is Lenovo"<<endl;};

}

class LenovoWithLinux: public LinuxOS,public Lenovo {

public:

  void InstallOS(){printInfo();Install();}

}

class LenovoWithWindows: public WindowsOS,public Lenovo {

public:

  void InstallOS(){printInfo();Install();}

}

int main(int argc,char **argv)

{

  MacWithLinux a;

  a.InstallOS();

  LenovoWithLinux b;

  b.InstallOS();

  return 0;

}

上面的这个例子太复杂了如果computer有M个派生类,OS有N个派生类,那么要维护一个M*N的派生类,对于computer和OS我们能否建立连接,也就是说搭一个桥,在computer里面建个指针指向OS就可以了

改进代码如下:

eg:using namespace std;

class OS{

public :

  virtual void installOS_impl() = 0;//纯虚函数

}

class LinuxOS : public OS{

public:

  virtual void installOS_impl(){cout<<"Install Linux OS"<<endl;}

}

class WindowsOS : public OS{

public:

  virtual void installOS_impl(){cout<<"Install Windows OS"<<endl;}

}

class Computer{

public:

  virtual void printInfo() = 0;

  virtual void InstallOS() = 0;

}

class MAC:public Computer{

public:

  virtual void printInfo(){cout<<"This isMac"<<endl;};

  MAC(OS *impl){this->impl = impl};

  void InstallOS(){printinfo();impl->installOS_impl();};

private:

  OS *impl;

}

class Lenovo:public Computer{

public:

  virtual void printInfo(){cout<<"This is Lenovo"<<endl;};

  Lenovo(OS *impl){this->impl = impl};

  void InstallOS(){printinfo();impl->installOS_impl();};

private:

  OS *impl;

}

int main(int argc,char **argv)

{

  OS *os1 = new linuxOS();

  OS *os2 = new WindowsOS();

  computer *c1 = new Mac(os1 );

  computer *c2 = new Lenovo(os2 );

  c1->InstallOS();

  c2->InstallOS();

return 0;

}

    Computer           OS                     

    InstallOS(抽象部分)                IntallOS_impl(实现部分)

  Mac              Lenovo      Linux   Windows

  在Mac里面存放指针:OS *impl  使用的时候让其指向new Linux或者new Windows

  这就是所谓的桥接关系

4、C++快速入门2的更多相关文章

  1. Web Api 入门实战 (快速入门+工具使用+不依赖IIS)

    平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html 屁话我也就不多说了,什么简介的也省了,直接简单概括+demo ...

  2. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  3. 前端开发小白必学技能—非关系数据库又像关系数据库的MongoDB快速入门命令(2)

    今天给大家道个歉,没有及时更新MongoDB快速入门的下篇,最近有点小忙,在此向博友们致歉.下面我将简单地说一下mongdb的一些基本命令以及我们日常开发过程中的一些问题.mongodb可以为我们提供 ...

  4. 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  5. 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  6. Mybatis框架 的快速入门

    MyBatis 简介 什么是 MyBatis? MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果 ...

  7. grunt快速入门

    快速入门 Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器. Grunt 0.4.x 必须配合Node.js >= 0.8.0版本使用.:奇数版本 ...

  8. 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  9. 【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  10. Vue.js 快速入门

    什么是Vue.js vue是法语中视图的意思,Vue.js是一个轻巧.高性能.可组件化的MVVM库,同时拥有非常容易上手的API.作者是尤雨溪,写下这篇文章时vue.js版本为1.0.7 准备 我推荐 ...

随机推荐

  1. amaze ui响应式表格

    amaze ui响应式表格 这里的div外嵌设置格式倒是不错的选择

  2. 体验 Windows 系统 CVM

    添加角色功能: service.msc 设置自动服务,net start telnet

  3. Python day4知识回顾

    # -*- coding: utf_8 _*_# Author:Vi#字典是无序的 info = { 'student001':"DIO", 'student002':" ...

  4. Android中级第十讲--相机录像和查看系统相册图片

    博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 录像比较简单,开始录制: myCamera.unlock(); ...

  5. [React] Use a Render Porp

    More detail check LInk. Render Prop vs HOC: HOC version for withMouse: import React from 'react' imp ...

  6. <memory>(包括了auto_ptr,shared_ptr等各种指针)

    Memory elements This header defines general utilities to manage dynamic memory: Allocators allocator ...

  7. nls 字符编码文件对应的国家语言

    原文 http://ftp.twaren.net/cpatch/faq/tech/tech_nlsnt.txt * updated by Kii Ali, 12-11-2001 ftp://ftp.n ...

  8. js17---创建对象:构造函数式和原型组合模式、动态原型模式、稳妥构造函数式

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  9. atxserver2安装与使用

    atxserver2的使用 1.首先clone atxserver2代码,此时使用pip3 install requirements后执行python main.py 会提示“ [WinError 1 ...

  10. C#之使用app.config可记录数据,下次打开可读取记录的数据

    一.背景 如下图所示,我通过open..按键打开了某个文件,之后我再把app给关闭掉,当再次打开app的时候,在textBox.Text上显示上一次打开的文件路径.通过使用app.config可以保存 ...