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. }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
常见C++ 设计模式有以下几种: 1. 工厂模式(Factory Pattern):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 2. 单例模式(Singleton Pattern):保证一个类仅有一个实例,并提供一个访问它的全局访问点。 3. 观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者都会收到通知并更新。 4. 适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 5. 装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。 6. 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们之间可以互换。 7. 模板方法模式(Template Method Pattern):定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 8. 建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 9. 迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。 10. 外观模式(Facade Pattern):为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值