C++002常见设计模式

饿汉式的单例模式

1.1 问题

单例模式使用私有构造函数的方法禁止在类外部创建实例,而类自己使用静态成员变量的方法来维护其唯一实例,并提供访问该实例的方法

饿汉式单例模式的优点是加载进程时静态创建单例对象,线程安全;缺点是无论使用与否,总要创建一个实例

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:饿汉式的单例模式

代码如下:

    
    
    
    
  1. #include <iostream>
  2. class Singleton
  3. {
  4. private:
  5. Singleton (void)
  6. {
  7. }
  8. Singleton (Singleton const& that)
  9. {
  10. }
  11. static Singleton s_instance;
  12. public:
  13. static Singleton& getInstance (void)
  14. {
  15. return s_instance;
  16. }
  17. };
  18. Singleton Singleton::s_instance;
  19. int main(int argc, const char * argv[])
  20. {
  21. Singleton& a = Singleton::getInstance();
  22. std::cout << std::hex << &a << std::endl;
  23. Singleton& b = Singleton::getInstance();
  24. std::cout << std::hex << &b << std::endl;
  25. return 0;
  26. }

上述代码中,以下代码:

    
    
    
    
  1. private:
  2. Singleton (void)
  3. {
  4. }
  5. Singleton (Singleton const& that)
  6. {
  7. }

通过将类Singleton的构造函数和拷贝构造函数定义成私有的,来禁止在类外创建该类的对象

上述代码中,以下代码:

    
    
    
    
  1. static Singleton s_instance;

将单例类的唯一对象实例,实现为其静态成员变量。虽然静态成员变量s_instance需要在类的外部实例化,但它毕竟是Singleton类的成员(尽管是静态成员),依然属于类的内部元素,可以调用该类的私有构造函数。

上述代码中,以下代码:

    
    
    
    
  1. static Singleton& getInstance (void)
  2. {
  3. return s_instance;
  4. }

通过成员函数getInstance来获得类Singleton的唯一对象实例。

1.3 完整代码

本案例的完整代码如下所示:

    
    
    
    
  1. #include <iostream>
  2. class Singleton
  3. {
  4. private:
  5. Singleton (void)
  6. {
  7. }
  8. Singleton (Singleton const& that)
  9. {
  10. }
  11. static Singleton s_instance;
  12. public:
  13. static Singleton& getInstance (void)
  14. {
  15. return s_instance;
  16. }
  17. };
  18. Singleton Singleton::s_instance;
  19. int main(int argc, const char * argv[])
  20. {
  21. Singleton& a = Singleton::getInstance();
  22. std::cout << std::hex << &a << std::endl;
  23. Singleton& b = Singleton::getInstance();
  24. std::cout << std::hex << &b << std::endl;
  25. return 0;
  26. }

2 懒汉式的单例模式

2.1 问题

单例模式使用私有构造函数的方法禁止在类外部创建实例,而类自己使用静态成员变量的方法来维护其唯一实例,并提供访问该实例的方法。

懒汉式单例模式的优点是用则创建,不用不创建,什么时候用什么时候创建;缺点是首次访问时动态创建单例对象,在多线程应用中,存在线程不安全的问题。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:懒汉式的单例模式

代码如下:

    
    
    
    
  1. #include <iostream>
  2. class Singleton
  3. {
  4. private:
  5. Singleton (void)
  6. {
  7. }
  8. Singleton (Singleton const& that)
  9. {
  10. }
  11. static Singleton* s_instance;
  12. public:
  13. static Singleton& getInstance (void)
  14. {
  15. if (! s_instance)
  16. s_instance = new Singleton;
  17. return *s_instance;
  18. }
  19. };
  20. Singleton* Singleton::s_instance = NULL;
  21. int main(int argc, const char * argv[])
  22. {
  23. Singleton& a = Singleton::getInstance();
  24. std::cout << std::hex << &a << std::endl;
  25. Singleton& b = Singleton::getInstance();
  26. std::cout << std::hex << &b << std::endl;
  27. return 0;
  28. }

上述代码中,以下代码:

    
    
    
    
  1. private:
  2. Singleton (void)
  3. {
  4. }
  5. Singleton (Singleton const& that)
  6. {
  7. }

通过将类Singleton的构造函数和拷贝构造函数定义成私有的,来禁止在类外创建该类的对象。

上述代码中,以下代码:

    
    
    
    
  1. static Singleton* s_instance;

声明了类Singleton的静态指针,该指针在类外定义时,被初始化为空,如以下代码所示:

    
    
    
    
  1. Singleton* Singleton::s_instance = NULL;

上述代码中,以下代码:

    
    
    
    
  1. static Singleton& getInstance (void)
  2. {
  3. if (! s_instance)
  4. s_instance = new Singleton;
  5. return *s_instance;
  6. }

通过成员函数getInstance来获得类Singleton的唯一对象实例。与饿汉式的区别是在函数getInstance第一次被调用时,才创建唯一对象实例,而不是在创建程序一开始就创建唯一对象实例。

2.3 完整代码

本案例的完整代码如下所示:

    
    
    
    
  1. #include <iostream>
  2. class Singleton
  3. {
  4. private:
  5. Singleton (void)
  6. {
  7. }
  8. Singleton (Singleton const& that)
  9. {
  10. }
  11. static Singleton* s_instance;
  12. public:
  13. static Singleton& getInstance (void)
  14. {
  15. if (! s_instance)
  16. s_instance = new Singleton;
  17. return *s_instance;
  18. }
  19. };
  20. Singleton* Singleton::s_instance = NULL;
  21. int main(int argc, const char * argv[])
  22. {
  23. Singleton& a = Singleton::getInstance();
  24. std::cout << std::hex << &a << std::endl;
  25. Singleton& b = Singleton::getInstance();
  26. std::cout << std::hex << &b << std::endl;
  27. return 0;
  28. }

3 考虑线程安全的单例模式

3.1 问题

饿汉式单例模式在多线程应用中,由于是多线程并发执行,有可能创建出多个对象实例,存在线程不安全的问题。借助互斥锁能够防止单例对象在不同线程中被重复创建。

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:考虑线程安全的单例模式

代码如下:

    
    
    
    
  1. #include <iostream>
  2. class Singleton
  3. {
  4. private:
  5. Singleton (void)
  6. {
  7. }
  8. Singleton (Singleton const& that)
  9. {
  10. }
  11. static Singleton* s_instance;
  12. static pthread_mutex_t s_mutex;
  13. public:
  14. static Singleton& getInstance (void)
  15. {
  16. if (! s_instance)
  17. {
  18. pthread_mutex_lock (&s_mutex);
  19. if (! s_instance)
  20. s_instance = new Singleton;
  21. pthread_mutex_unlock (&s_mutex);
  22. }
  23. return *s_instance;
  24. }
  25. };
  26. Singleton* Singleton::s_instance = NULL;
  27. pthread_mutex_t Singleton::s_mutex = PTHREAD_MUTEX_INITIALIZER;_
  28. int main(int argc, const char * argv[])
  29. {
  30. Singleton& a = Singleton::getInstance();
  31. std::cout << std::hex << &a << std::endl;
  32. Singleton& b = Singleton::getInstance();
  33. std::cout << std::hex << &b << std::endl;
  34. return 0;
  35. }

上述代码中,以下代码:

    
    
    
    
  1. static Singleton& getInstance (void)
  2. {
  3. if (! s_instance)
  4. {
  5. pthread_mutex_lock (&s_mutex);
  6. if (! s_instance)
  7. s_instance = new Singleton;
  8. pthread_mutex_unlock (&s_mutex);
  9. }
  10. return *s_instance;
  11. }

在获得类Singleton的唯一对象实例的成员函数getInstance中,通过加互斥锁的方法来保证多线程安全。如下代码:

    
    
    
    
  1. pthread_mutex_lock (&s_mutex);

首先设置互斥变量s_mutex,此时如果有其它线程已经设置了该变量,则当前线程就等待,直到其他线程释放互斥变量,此线程才重新开始执行。

以下代码:

    
    
    
    
  1. pthread_mutex_unlock (&s_mutex);

是释放互斥变量s_mutex。

3.3 完整代码

本案例的完整代码如下所示:

    
    
    
    
  1. #include <iostream>
  2. class Singleton
  3. {
  4. private:
  5. Singleton (void)
  6. {
  7. }
  8. Singleton (Singleton const& that)
  9. {
  10. }
  11. static Singleton* s_instance;
  12. static pthread_mutex_t s_mutex;
  13. public:
  14. static Singleton& getInstance (void)
  15. {
  16. if (! s_instance)
  17. {
  18. pthread_mutex_lock (&s_mutex);
  19. if (! s_instance)
  20. s_instance = new Singleton;
  21. pthread_mutex_unlock (&s_mutex);
  22. }
  23. return *s_instance;
  24. }
  25. };
  26. Singleton* Singleton::s_instance = NULL;
  27. pthread_mutex_t Singleton::s_mutex = PTHREAD_MUTEX_INITIALIZER;_
  28. int main(int argc, const char * argv[])
  29. {
  30. Singleton& a = Singleton::getInstance();
  31. std::cout << std::hex << &a << std::endl;
  32. Singleton& b = Singleton::getInstance();
  33. std::cout << std::hex << &b << std::endl;
  34. return 0;
  35. }

4 通过成员指针访问对象

4.1 问题

成员变量指针的本质,就是特定成员变量在类对象实例中的相对地址。成员变量指针解引用,就是根据类对象实例的起始地址,结合成员变量指针中的相对地址,计算出具体成员变量的绝对地址,并访问之。类的静态成员变量不是对象的一部分,不需要根据相对地址计算绝对地址,也不需要通过对象或其指针解引用。

成员函数并不存储在对象中,不存在根据相对地址计算绝对地址的问题,但是需要通过对象或对象指针对成员函数指针解引用,其目的只有一个,即提供this指针。类的静态成员函数没有this指针,无需调用对象。

静态成员与对象无关,因此静态成员指针与普通指针并没有任何本质性区别。

4.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:通过成员变量指针访问成员变量

代码如下:

    
    
    
    
  1. #include <iostream>
  2. class Student
  3. {
  4. public:
  5. std::string m_name;
  6. std::string m_sex;
  7. int m_age;
  8. int m_no;
  9. };
  10. int main(int argc, const char * argv[])
  11. {
  12. int Student::*p_age = &Student::m_age;
  13. Student student;
  14. student.*p_age = 10;
  15. std::cout << student.m_age << std::endl;
  16. return 0;
  17. }

上述代码中,以下代码:

    
    
    
    
  1. int Student::*p_age = &Student::m_age;

定义了一个成员变量指针p_age,指向类Student的变量成员m_age.

上述代码中,以下代码:

    
    
    
    
  1. Student student;
  2. student.*p_age = 10;

定义了一个类Student的对象student,然后通过成员变量指针p_age,将对象student中的变量成员m_age赋值为10,这被称为解引用。其实,成员变量指针p_age指向的是变量成员m_age在对象student中的相对地址。“.*”是一个独立的运算符,成员指针解引用运算符。

步骤二:通过成员函数指针访问成员函数

代码如下:

    
    
    
    
  1. #include <iostream>
  2. class Student
  3. {
  4. public:
  5. std::string m_name;
  6. std::string m_sex;
  7. int m_age;
  8. int m_no;
  9. void who (void) const
  10. {
  11. std::cout << "我是" << m_name << ",性别" << m_sex << ",今年" << m_age << "岁,学号" << m_no << std::endl;
  12. }
  13. };
  14. int main(int argc, const char * argv[])
  15. {
  16. int Student::*p_age = &Student::m_age;
  17. Student student;
  18. student.*p_age = 10;
  19. std::cout << student.m_age << std::endl;
  20. student.m_name = "张三";
  21. student.m_no = 12;
  22. student.m_sex = "男";
  23. void (Student::*p_who) (void) const = &Student::who;
  24. (student.*p_who)();
  25. return 0;
  26. }

上述代码中,以下代码:

    
    
    
    
  1. void (Student::*p_who) (void) const = &Student::who;

定义了一个成员函数指针p_who,指向类Student的函数成员who。

上述代码中,以下代码:

    
    
    
    
  1. (student.*p_who)();

通过成员函数指针p_who,调用类Student的成员函数who,这被称为解引用。添加student的目的只有一个,即提供this指针。

步骤三:通过静态成员指针访问静态成员

代码如下:

    
    
    
    
  1. #include <iostream>
  2. class Student
  3. {
  4. public:
  5. std::string m_name;
  6. std::string m_sex;
  7. int m_age;
  8. int m_no;
  9. static int s_count;
  10. void who (void) const
  11. {
  12. std::cout << "我是" << m_name << ",性别" << m_sex << ",今年" << m_age << "岁,学号" << m_no << std::endl;
  13. }
  14. static void inc (void)
  15. {
  16. s_count++;
  17. }
  18. };
  19. int Student::s_count = 0;
  20. int main(int argc, const char * argv[])
  21. {
  22. int Student::*p_age = &Student::m_age;
  23. Student student;
  24. student.*p_age = 10;
  25. std::cout << "age = " << student.m_age << std::endl;
  26. student.m_name = "张三";
  27. student.m_no = 12;
  28. student.m_sex = "男";
  29. void (Student::*p_who) (void) const = &Student::who;
  30. (student.*p_who)();
  31. int* p_count = &Student::s_count;
  32. *p_count = 10;
  33. std::cout << "count = " << Student::s_count << std::endl;
  34. void (*p_inc)(void) = Student::inc;
  35. (*p_inc)();
  36. std::cout << "count = " << Student::s_count << std::endl;
  37. return 0;
  38. }

上述代码中,以下代码:

    
    
    
    
  1. int* p_count = &Student::s_count;
  2. *p_count = 10;

定义了一个静态成员指针变量p_count,指向类Student的静态变量成员s_count。静态成员指针与普通指针并没有任何本质性区别,只是成员指针受访问控制属性约束。

上述代码中,以下代码:

    
    
    
    
  1. void (*p_inc)(void) = Student::inc;

定义了一个静态成员函数指针p_inc,指向类Student的函数成员inc。

上述代码中,以下代码:

    
    
    
    
  1. (*p_inc)();

通过静态成员函数指针p_inc,调用类Student的成员函数inc。类的静态成员函数没有this指针,无需调用对象。

4.3 完整代码

本案例的完整代码如下所示:

    
    
    
    
  1. #include <iostream>
  2. class Student
  3. {
  4. public:
  5. std::string m_name;
  6. std::string m_sex;
  7. int m_age;
  8. int m_no;
  9. static int s_count;
  10. void who (void) const
  11. {
  12. std::cout << "我是" << m_name << ",性别" << m_sex << ",今年" << m_age << "岁,学号" << m_no << std::endl;
  13. }
  14. static void inc (void)
  15. {
  16. s_count++;
  17. }
  18. };
  19. int Student::s_count = 0;
  20. int main(int argc, const char * argv[])
  21. {
  22. int Student::*p_age = &Student::m_age;
  23. Student student;
  24. student.*p_age = 10;
  25. std::cout << "age = " << student.m_age << std::endl;
  26. student.m_name = "张三";
  27. student.m_no = 12;
  28. student.m_sex = "男";
  29. void (Student::*p_who) (void) const = &Student::who;
  30. (student.*p_who)();
  31. int* p_count = &Student::s_count;
  32. *p_count = 10;
  33. std::cout << "count = " << Student::s_count << std::endl;
  34. void (*p_inc)(void) = Student::inc;
  35. (*p_inc)();
  36. std::cout << "count = " << Student::s_count << std::endl;
  37. return 0;
  38. }

 简单工厂模式

11.1 问题

全部由纯虚函数构成的抽象类称为纯抽象类或接口。面向抽象编程,使得所有基于接口编写的代码,在子类被更替后,无需做任何修改或只需做很少的修改,就能在新子类上正确运行。

11.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:简单工厂模式

代码如下:

        
        
        
        
  1. #include <iostream>
  2. typedef enum ProductTypeTag
  3. {
  4. TypeA,
  5. TypeB,
  6. TypeC
  7. }PRODUCTTYPE;
  8. // Here is the product class
  9. class Product
  10. {
  11. public:
  12. virtual void show() = 0;
  13. };
  14. class ProductA : public Product
  15. {
  16. public:
  17. void show(void)
  18. {
  19. std::cout << "I'm ProductA" << std::endl;
  20. }
  21. };
  22. class ProductB : public Product
  23. {
  24. public:
  25. void show(void)
  26. {
  27. std::cout << "I'm ProductB" << std::endl;
  28. }
  29. };
  30. class ProductC : public Product
  31. {
  32. public:
  33. void show(void)
  34. {
  35. std::cout << "I'm ProductC" << std::endl;
  36. }
  37. };
  38. // Here is the Factory class
  39. class Factory
  40. {
  41. public:
  42. Product* CreateProduct(PRODUCTTYPE type)
  43. {
  44. switch (type)
  45. {
  46. case TypeA:
  47. return new ProductA;
  48. case TypeB:
  49. return new ProductB;
  50. case TypeC:
  51. return new ProductC;
  52. default:
  53. return NULL;
  54. }
  55. }
  56. };
  57. int main(int argc, char *argv[])
  58. {
  59. // First, create a factory object
  60. Factory *ProductFactory = new Factory();
  61. Product *productObjA = ProductFactory->CreateProduct(TypeA);
  62. if (productObjA != NULL)
  63. productObjA->show();
  64. Product *productObjB = ProductFactory->CreateProduct(TypeB);
  65. if (productObjB != NULL)
  66. productObjB->show();
  67. Product *productObjC = ProductFactory->CreateProduct(TypeC);
  68. if (productObjC != NULL)
  69. productObjC->show();
  70. delete ProductFactory;
  71. delete productObjA;
  72. delete productObjB;
  73. delete productObjC;
  74. return 0;
  75. }

上述代码中,以下代码:

        
        
        
        
  1. class Product
  2. {
  3. public:
  4. virtual void show() = 0;
  5. };

定义了一个抽象产品类Product。

上述代码中,以下代码:

        
        
        
        
  1. class ProductA : public Product
  2. {
  3. public:
  4. void show(void)
  5. {
  6. std::cout << "I'm ProductA" << std::endl;
  7. }
  8. };
  9. class ProductB : public Product
  10. {
  11. public:
  12. void show(void)
  13. {
  14. std::cout << "I'm ProductB" << std::endl;
  15. }
  16. };
  17. class ProductC : public Product
  18. {
  19. public:
  20. void show(void)
  21. {
  22. std::cout << "I'm ProductC" << std::endl;
  23. }
  24. };

定义了3个具体产品类。在这3个类中,分别对纯虚函数show进行了实现。还可能有更多的具体产品类。这样就造成创建对象时,对象多而杂。

上述代码中,以下代码:

        
        
        
        
  1. class Factory
  2. {
  3. public:
  4. Product* CreateProduct(PRODUCTTYPE type)
  5. {
  6. switch (type)
  7. {
  8. case TypeA:
  9. return new ProductA;
  10. case TypeB:
  11. return new ProductB;
  12. case TypeC:
  13. return new ProductC;
  14. default:
  15. return NULL;
  16. }
  17. }
  18. };

定义了一个简单工厂类Factory,其中含有一个成员函数CreateProduct用于生成具体产品对象,以简化创建对象时,对象多而杂的现象。这样就能把对象的创建和操作两部分分离开,方便后期的程序扩展和维护。

上述代码中,以下代码:

        
        
        
        
  1. Factory *ProductFactory = new Factory();
  2. Product *productObjA = ProductFactory->CreateProduct(TypeA);
  3. if (productObjA != NULL)
  4. productObjA->show();

在主程序中,使用简单工厂类Factory的CreateProduct函数生成具体产品对象,用抽象基类Product的指针实现多态。

11.3 完整代码

本案例的完整代码如下所示:

        
        
        
        
  1. #include <iostream>
  2. typedef enum ProductTypeTag
  3. {
  4. TypeA,
  5. TypeB,
  6. TypeC
  7. }PRODUCTTYPE;
  8. // Here is the product class
  9. class Product
  10. {
  11. public:
  12. virtual void show() = 0;
  13. };
  14. class ProductA : public Product
  15. {
  16. public:
  17. void show(void)
  18. {
  19. std::cout << "I'm ProductA" << std::endl;
  20. }
  21. };
  22. class ProductB : public Product
  23. {
  24. public:
  25. void show(void)
  26. {
  27. std::cout << "I'm ProductB" << std::endl;
  28. }
  29. };
  30. class ProductC : public Product
  31. {
  32. public:
  33. void show(void)
  34. {
  35. std::cout << "I'm ProductC" << std::endl;
  36. }
  37. };
  38. // Here is the Factory class
  39. class Factory
  40. {
  41. public:
  42. Product* CreateProduct(PRODUCTTYPE type)
  43. {
  44. switch (type)
  45. {
  46. case TypeA:
  47. return new ProductA;
  48. case TypeB:
  49. return new ProductB;
  50. case TypeC:
  51. return new ProductC;
  52. default:
  53. return NULL;
  54. }
  55. }
  56. };
  57. int main(int argc, char *argv[])
  58. {
  59. // First, create a factory object
  60. Factory *ProductFactory = new Factory();
  61. Product *productObjA = ProductFactory->CreateProduct(TypeA);
  62. if (productObjA != NULL)
  63. productObjA->show();
  64. Product *productObjB = ProductFactory->CreateProduct(TypeB);
  65. if (productObjB != NULL)
  66. productObjB->show();
  67. Product *productObjC = ProductFactory->CreateProduct(TypeC);
  68. if (productObjC != NULL)
  69. productObjC->show();
  70. delete ProductFactory;
  71. delete productObjA;
  72. delete productObjB;
  73. delete productObjC;
  74. return 0;
  75. }

12 模板方法模式

12.1 问题

全部由纯虚函数构成的抽象类称为纯抽象类或接口。面向抽象编程,使得所有基于接口编写的代码,在子类被更替后,无需做任何修改或只需做很少的修改,就能在新子类上正确运行。

12.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:模板方法模式

代码如下:

        
        
        
        
  1. #include <iostream>
  2. class Abstract
  3. {
  4. protected:
  5. virtual void PrimitiveOperation1() = 0;
  6. virtual void PrimitiveOperation2() = 0;
  7. public:
  8. void TemplateMethod()
  9. {
  10. std::cout << "TemplateMethod" << std::endl;
  11. PrimitiveOperation1();
  12. PrimitiveOperation2();
  13. }
  14. };
  15. class ConcreteA : public Abstract
  16. {
  17. protected:
  18. virtual void PrimitiveOperation1()
  19. {
  20. std::cout << "ConcreteA Operation1" << std::endl;
  21. }
  22. virtual void PrimitiveOperation2()
  23. {
  24. std::cout << "ConcreteA Operation2" << std::endl;
  25. }
  26. };
  27. class ConcreteB : public Abstract
  28. {
  29. protected:
  30. virtual void PrimitiveOperation1()
  31. {
  32. std::cout << "ConcreteB Operation1" << std::endl;
  33. }
  34. virtual void PrimitiveOperation2()
  35. {
  36. std::cout << "ConcreteB Operation2" << std::endl;
  37. }
  38. };
  39. int main()
  40. {
  41. Abstract *pAbstractA = new ConcreteA;
  42. pAbstractA->TemplateMethod();
  43. Abstract *pAbstractB = new ConcreteB;
  44. pAbstractB->TemplateMethod();
  45. delete pAbstractA;
  46. delete pAbstractB;
  47. }

上述代码中,以下代码:

        
        
        
        
  1. class Abstract
  2. {
  3. protected:
  4. virtual void PrimitiveOperation1() = 0;
  5. virtual void PrimitiveOperation2() = 0;
  6. public:
  7. void TemplateMethod()
  8. {
  9. std::cout << "TemplateMethod" << std::endl;
  10. PrimitiveOperation1();
  11. PrimitiveOperation2();
  12. }
  13. };

定义了抽象模板类Abstract。在该类中,定义两个抽象的原语操作纯虚函数PrimitiveOperation1和纯虚函数PrimitiveOperation2,用这两个原语操作定义一个抽象算法的两个步骤。在该类中还定义了一个模板成员函数TemplateMethod负责整合两个原语操作及其它一些操作,形成一个不变的算法流程。

上述代码中,以下代码:

        
        
        
        
  1. class ConcreteA : public Abstract
  2. {
  3. protected:
  4. virtual void PrimitiveOperation1()
  5. {
  6. std::cout << "ConcreteA Operation1" << std::endl;
  7. }
  8. virtual void PrimitiveOperation2()
  9. {
  10. std::cout << "ConcreteA Operation2" << std::endl;
  11. }
  12. };
  13. class ConcreteB : public Abstract
  14. {
  15. protected:
  16. virtual void PrimitiveOperation1()
  17. {
  18. std::cout << "ConcreteB Operation1" << std::endl;
  19. }
  20. virtual void PrimitiveOperation2()
  21. {
  22. std::cout << "ConcreteB Operation2" << std::endl;
  23. }
  24. };

定义了两个实体类,在这两个实体类中,各自实现两个具体的原语操作,完成各自实体中的具体算法的各步骤的具体操作。这样如果再有其它的具体算法,只需要再定义一个实体类,实现具体的原语操作即可。

上述代码中,以下代码:

        
        
        
        
  1. Abstract *pAbstractA = new ConcreteA;
  2. pAbstractA->TemplateMethod();

在主程序中,用抽象模板类Abstract的指针指向实体类ConcreteA,完成一个具体的算法。

12.3 完整代码

本案例的完整代码如下所示:

        
        
        
        
  1. #include <iostream>
  2. class Abstract
  3. {
  4. protected:
  5. virtual void PrimitiveOperation1() = 0;
  6. virtual void PrimitiveOperation2() = 0;
  7. public:
  8. void TemplateMethod()
  9. {
  10. std::cout << "TemplateMethod" << std::endl;
  11. PrimitiveOperation1();
  12. PrimitiveOperation2();
  13. }
  14. };
  15. class ConcreteA : public Abstract
  16. {
  17. protected:
  18. virtual void PrimitiveOperation1()
  19. {
  20. std::cout << "ConcreteA Operation1" << std::endl;
  21. }
  22. virtual void PrimitiveOperation2()
  23. {
  24. std::cout << "ConcreteA Operation2" << std::endl;
  25. }
  26. };
  27. class ConcreteB : public Abstract
  28. {
  29. protected:
  30. virtual void PrimitiveOperation1()
  31. {
  32. std::cout << "ConcreteB Operation1" << std::endl;
  33. }
  34. virtual void PrimitiveOperation2()
  35. {
  36. std::cout << "ConcreteB Operation2" << std::endl;
  37. }
  38. };
  39. int main()
  40. {
  41. Abstract *pAbstractA = new ConcreteA;
  42. pAbstractA->TemplateMethod();
  43. Abstract *pAbstractB = new ConcreteB;
  44. pAbstractB->TemplateMethod();
  45. delete pAbstractA;
  46. delete pAbstractB;
  47. }

13 单继承虚表模型

13.1 问题

编译器会为每个包含虚函数的类生成一张虚函数表,即存放每个虚函数地址的函数指针数组,简称虚表(vtbl)。

13.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:单继承虚表模型

代码如下:

        
        
        
        
  1. #include <iostream>
  2. class Base
  3. {
  4. public:
  5. virtual int f1 (void)
  6. {
  7. std::cout << "Base f1" << std::endl;
  8. return 0;
  9. }
  10. virtual void f2 (int i)
  11. {
  12. std::cout << "Base f2" << std::endl;
  13. }
  14. virtual int f3 (int i)
  15. {
  16. std::cout << "Base f3" << std::endl;
  17. return 0;
  18. }
  19. };
  20. class Derived : public Base
  21. {
  22. public:
  23. int f1 (void)
  24. {
  25. std::cout << "Derived f1" << std::endl;
  26. return 0;
  27. }
  28. int f3 (int i)
  29. {
  30. std::cout << "Derived f3" << std::endl;
  31. return 0;
  32. }
  33. virtual void f4 (void)
  34. {
  35. std::cout << "Derived f4" << std::endl;
  36. }
  37. };
  38. int main()
  39. {
  40. Base *b = new Base;
  41. b->f3(10);
  42. delete b;
  43. b = new Derived;
  44. b->f3(20);
  45. delete b;
  46. }

上述代码中,以下代码:

        
        
        
        
  1. class Base
  2. {
  3. public:
  4. virtual int f1 (void)
  5. {
  6. std::cout << "Base f1" << std::endl;
  7. return 0;
  8. }
  9. virtual void f2 (int i)
  10. {
  11. std::cout << "Base f2" << std::endl;
  12. }
  13. virtual int f3 (int i)
  14. {
  15. std::cout << "Base f3" << std::endl;
  16. return 0;
  17. }
  18. };

定义了一个类Base,该类中含有三个虚函数。编译器会为类Base生成一张虚函数表,该表是用于存放每个虚函数地址的函数指针数组,简称虚表(vtbl),每个虚函数对应一个虚函数表中的数组元素。即vtbl[0]->f1,vtbl[1]->f2,vtbl[2]->f3。

除了为类Base生成虚函数表以外,编译器还会为类Base增加一个隐式的成员变量,通常在该类实例化对象的起始位置,用于存放虚函数表的首地址,该变量被称为虚函数表指针,简称虚指针(vptr)。即vptr = &vtbl[0]。

这样,主程序中以下语句:

        
        
        
        
  1. Base *b = new Base;
  2. b->f3(10);

相当于:

        
        
        
        
  1. Base *b = new Base;
  2. b->vptr[2](10);

虚表是一个类一张,而不是一个对象一张,同一个类的多个对象,通过各自的虚指针,共享同一张虚表。

上述代码中,以下代码:

        
        
        
        
  1. class Derived : public Base
  2. {
  3. public:
  4. int f1 (void)
  5. {
  6. std::cout << "Derived f1" << std::endl;
  7. return 0;
  8. }
  9. int f3 (int i)
  10. {
  11. std::cout << "Derived f3" << std::endl;
  12. return 0;
  13. }
  14. virtual void f4 (void)
  15. {
  16. std::cout << "Derived f4" << std::endl;
  17. }
  18. };

定义了子类Derived,并且在子类Derived中覆盖了基类Base的f1和f3,继承了基类Base的f2,增加了自己的f4,编译器同样会为子类生成一张专属于它的虚表。指向子类Derived虚表的虚指针就存放在子类对象的基类子对象中,通常还是在起始位置。

这样,主程序中以下语句:

        
        
        
        
  1. b = new Derived;
  2. b->f3(20);

相当于:

        
        
        
        
  1. b = new Derived;
  2. b->vptr[2](20);

此时vptr[2]中存放的是子类Derived中覆盖基类Base的f3,而这就是所谓的多态。

13.3 完整代码

本案例的完整代码如下所示:

        
        
        
        
  1. #include <iostream>
  2. class Base
  3. {
  4. public:
  5. virtual int f1 (void)
  6. {
  7. std::cout << "Base f1" << std::endl;
  8. return 0;
  9. }
  10. virtual void f2 (int i)
  11. {
  12. std::cout << "Base f2" << std::endl;
  13. }
  14. virtual int f3 (int i)
  15. {
  16. std::cout << "Base f3" << std::endl;
  17. return 0;
  18. }
  19. };
  20. class Derived : public Base
  21. {
  22. public:
  23. int f1 (void)
  24. {
  25. std::cout << "Derived f1" << std::endl;
  26. return 0;
  27. }
  28. int f3 (int i)
  29. {
  30. std::cout << "Derived f3" << std::endl;
  31. return 0;
  32. }
  33. virtual void f4 (void)
  34. {
  35. std::cout << "Derived f4" << std::endl;
  36. }
  37. };
  38. int main()
  39. {
  40. Base *b = new Base;
  41. b->f3(10);
  42. delete b;
  43. b = new Derived;
  44. b->f3(20);
  45. delete b;
  46. }

14 动态类型转换

14.1 问题

动态类型转换(dynamic_cast)用于将基类类型的指针或引用转换为其子类类型的指针或引用,前提是子类必须从基类多态继承,即基类包含至少一个虚函数

14.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:动态类型转换

代码如下:

        
        
        
        
  1. #include <iostream>
  2. class Base
  3. {
  4. public:
  5. virtual int f1 (void)
  6. {
  7. std::cout << "Base f1" << std::endl;
  8. return 0;
  9. }
  10. };
  11. class Derived : public Base
  12. {
  13. public:
  14. int f1 (void)
  15. {
  16. std::cout << "Derived f1" << std::endl;
  17. return 0;
  18. }
  19. };
  20. int main()
  21. {
  22. Derived d;
  23. Base* pa = &d;
  24. Derived* pb = dynamic_cast<Derived*> (pa);
  25. if (pb == NULL)
  26. std::cout << "转换失败!" << std::endl;
  27. Base& ra = d;
  28. try {
  29. Derived& rb = dynamic_cast<Derived&> (ra);
  30. }
  31. catch (std::bad_cast& ex)
  32. {
  33. std::cout << "转换失败!" << std::endl;
  34. }
  35. Derived b = dynamic_cast<Derived> (*pa);
  36. return 0;
  37. }

上述代码中,以下代码:

        
        
        
        
  1. Derived d;
  2. Base* pa = &d;
  3. Derived* pb = dynamic_cast<Derived*> (pa);

应用动态类型转换,将基类Base类型的指针转换为其子类Derived类型的指针。

上述代码中,以下代码:

        
        
        
        
  1. if (pb == NULL)
  2. std::cout << "转换失败!" << std::endl;

针对指针的动态类型转换,以返回空指针(NULL)表示失败。

上述代码中,以下代码:

        
        
        
        
  1. Base& ra = d;
  2. try {
  3. Derived& rb = dynamic_cast<Derived&> (ra);
  4. }

应用动态类型转换,将基类Base类型的引用转换为其子类Derived类型的引用。

上述代码中,以下代码:

        
        
        
        
  1. Base& ra = d;
  2. try {
  3. Derived& rb = dynamic_cast<Derived&> (ra);
  4. }
  5. catch (std::bad_cast& ex)
  6. {
  7. std::cout << "转换失败!" << std::endl;
  8. }

针对引用的动态类型转换,以抛出bad_cast异常表示失败。

上述代码中,以下代码:

        
        
        
        
  1. Derived b = dynamic_cast<Derived> (*pa);

不是针对指针或引用做的动态类型转换,编译错误。另外,要注意,转换目标类型和源类型之间不具有多态继承性也会发生编译错误。转换源类型的目标对象非目标类型会发生运行错误。

14.3 完整代码

本案例的完整代码如下所示:

        
        
        
        
  1. #include <iostream>
  2. class Base
  3. {
  4. public:
  5. virtual int f1 (void)
  6. {
  7. std::cout << "Base f1" << std::endl;
  8. return 0;
  9. }
  10. };
  11. class Derived : public Base
  12. {
  13. public:
  14. int f1 (void)
  15. {
  16. std::cout << "Derived f1" << std::endl;
  17. return 0;
  18. }
  19. };
  20. int main()
  21. {
  22. Derived d;
  23. Base* pa = &d;
  24. Derived* pb = dynamic_cast<Derived*> (pa);
  25. if (pb == NULL)
  26. std::cout << "转换失败!" << std::endl;
  27. Base& ra = d;
  28. try {
  29. Derived& rb = dynamic_cast<Derived&> (ra);
  30. }
  31. catch (std::bad_cast& ex)
  32. {
  33. std::cout << "转换失败!" << std::endl;
  34. }
  35. Derived b = dynamic_cast<Derived> (*pa);
  36. return 0;
  37. }

15 类型信息

15.1 问题

typeid操作符既可用于类型也可用于对象,用于返回类型信息,即类typeinfo对象的常引用。类typeinfo中含所有一个成员函数name,通过它可以得到类型名。

15.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:类型信息

代码如下:

        
        
        
        
  1. #include <iostream>
  2. class A
  3. {
  4. };
  5. class B : public A
  6. {
  7. public:
  8. virtual void foo (void)
  9. {
  10. }
  11. };
  12. class C : public B
  13. {
  14. };
  15. int main()
  16. {
  17. int x;
  18. int * px;
  19. std::cout << "int is: " << typeid(int).name() << std::endl;
  20. std::cout << " x is: " << typeid(x).name() << std::endl;
  21. std::cout << " px is: " << typeid(px).name() << std::endl;
  22. std::cout << "*px is: " << typeid(*px).name() << std::endl;
  23. C c;
  24. A& a = c;
  25. std::cout << typeid (a).name () << std::endl;
  26. B& b = c;
  27. std::cout << typeid (b).name () << std::endl;
  28. return 0;
  29. }

上述代码中,以下代码:

        
        
        
        
  1. int x;
  2. int * px;
  3. std::cout << "int is: " << typeid(int).name() << std::endl;
  4. std::cout << " x is: " << typeid(x).name() << std::endl;
  5. std::cout << " px is: " << typeid(px).name() << std::endl;
  6. std::cout << "*px is: " << typeid(*px).name() << std::endl;

使用typeid获取指定目标的类型信息。如:

        
        
        
        
  1. std::cout << "int is: " << typeid(int).name() << std::endl;

的输出结果为:int is: i。

上述代码中,以下代码:

        
        
        
        
  1. C c;
  2. A& a = c;
  3. std::cout << typeid (a).name () << std::endl;

当typeid作用于基类A类型的引用的目标c时,若基类A中不包含任何虚函数,则该操作符所返回的类型信息由引用A本身的类型决定,即返回的是类A的类名A。

上述代码中,以下代码:

        
        
        
        
  1. B& b = c;
  2. std::cout << typeid (b).name () << std::endl;

当typeid作用于基类B类型的引用的目标c时,若基类包含至少一个虚函数,即存在多态继承,该操作符所返回的类型信息由该引用的实际目标对象的类型决定,即返回的是类C的类名C。

15.3 完整代码

本案例的完整代码如下所示:

        
        
        
        
  1. #include <iostream>
  2. class A
  3. {
  4. };
  5. class B : public A
  6. {
  7. public:
  8. virtual void foo (void)
  9. {
  10. }
  11. };
  12. class C : public B
  13. {
  14. };
  15. int main()
  16. {
  17. int x;
  18. int * px;
  19. std::cout << "int is: " << typeid(int).name() << std::endl;
  20. std::cout << " x is: " << typeid(x).name() << std::endl;
  21. std::cout << " px is: " << typeid(px).name() << std::endl;
  22. std::cout << "*px is: " << typeid(*px).name() << std::endl;
  23. C c;
  24. A& a = c;
  25. std::cout << typeid (a).name () << std::endl;
  26. B& b = c;
  27. std::cout << typeid (b).name () << std::endl;
  28. return 0;
  29. }

你可能感兴趣的:(C++)