单例模式使用私有构造函数的方法禁止在类外部创建实例,而类自己使用静态成员变量的方法来维护其唯一实例,并提供访问该实例的方法。
饿汉式单例模式的优点是加载进程时静态创建单例对象,线程安全;缺点是无论使用与否,总要创建一个实例。
实现此案例需要按照如下步骤进行。
步骤一:饿汉式的单例模式
代码如下:
- #include <iostream>
- class Singleton
- {
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
- static Singleton s_instance;
- public:
- static Singleton& getInstance (void)
- {
- return s_instance;
- }
- };
- Singleton Singleton::s_instance;
- int main(int argc, const char * argv[])
- {
- Singleton& a = Singleton::getInstance();
- std::cout << std::hex << &a << std::endl;
- Singleton& b = Singleton::getInstance();
- std::cout << std::hex << &b << std::endl;
- return 0;
- }
上述代码中,以下代码:
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
通过将类Singleton的构造函数和拷贝构造函数定义成私有的,来禁止在类外创建该类的对象。
上述代码中,以下代码:
- static Singleton s_instance;
将单例类的唯一对象实例,实现为其静态成员变量。虽然静态成员变量s_instance需要在类的外部实例化,但它毕竟是Singleton类的成员(尽管是静态成员),依然属于类的内部元素,可以调用该类的私有构造函数。
上述代码中,以下代码:
- static Singleton& getInstance (void)
- {
- return s_instance;
- }
通过成员函数getInstance来获得类Singleton的唯一对象实例。
本案例的完整代码如下所示:
- #include <iostream>
- class Singleton
- {
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
- static Singleton s_instance;
- public:
- static Singleton& getInstance (void)
- {
- return s_instance;
- }
- };
- Singleton Singleton::s_instance;
- int main(int argc, const char * argv[])
- {
- Singleton& a = Singleton::getInstance();
- std::cout << std::hex << &a << std::endl;
- Singleton& b = Singleton::getInstance();
- std::cout << std::hex << &b << std::endl;
- return 0;
- }
单例模式使用私有构造函数的方法禁止在类外部创建实例,而类自己使用静态成员变量的方法来维护其唯一实例,并提供访问该实例的方法。
懒汉式单例模式的优点是用则创建,不用不创建,什么时候用什么时候创建;缺点是首次访问时动态创建单例对象,在多线程应用中,存在线程不安全的问题。
实现此案例需要按照如下步骤进行。
步骤一:懒汉式的单例模式
代码如下:
- #include <iostream>
- class Singleton
- {
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
- static Singleton* s_instance;
- public:
- static Singleton& getInstance (void)
- {
- if (! s_instance)
- s_instance = new Singleton;
- return *s_instance;
- }
- };
- Singleton* Singleton::s_instance = NULL;
- int main(int argc, const char * argv[])
- {
- Singleton& a = Singleton::getInstance();
- std::cout << std::hex << &a << std::endl;
- Singleton& b = Singleton::getInstance();
- std::cout << std::hex << &b << std::endl;
- return 0;
- }
上述代码中,以下代码:
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
通过将类Singleton的构造函数和拷贝构造函数定义成私有的,来禁止在类外创建该类的对象。
上述代码中,以下代码:
- static Singleton* s_instance;
声明了类Singleton的静态指针,该指针在类外定义时,被初始化为空,如以下代码所示:
- Singleton* Singleton::s_instance = NULL;
上述代码中,以下代码:
- static Singleton& getInstance (void)
- {
- if (! s_instance)
- s_instance = new Singleton;
- return *s_instance;
- }
通过成员函数getInstance来获得类Singleton的唯一对象实例。与饿汉式的区别是在函数getInstance第一次被调用时,才创建唯一对象实例,而不是在创建程序一开始就创建唯一对象实例。
本案例的完整代码如下所示:
- #include <iostream>
- class Singleton
- {
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
- static Singleton* s_instance;
- public:
- static Singleton& getInstance (void)
- {
- if (! s_instance)
- s_instance = new Singleton;
- return *s_instance;
- }
- };
- Singleton* Singleton::s_instance = NULL;
- int main(int argc, const char * argv[])
- {
- Singleton& a = Singleton::getInstance();
- std::cout << std::hex << &a << std::endl;
- Singleton& b = Singleton::getInstance();
- std::cout << std::hex << &b << std::endl;
- return 0;
- }
饿汉式单例模式在多线程应用中,由于是多线程并发执行,有可能创建出多个对象实例,存在线程不安全的问题。借助互斥锁能够防止单例对象在不同线程中被重复创建。
实现此案例需要按照如下步骤进行。
步骤一:考虑线程安全的单例模式
代码如下:
- #include <iostream>
- class Singleton
- {
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
- static Singleton* s_instance;
- static pthread_mutex_t s_mutex;
- public:
- static Singleton& getInstance (void)
- {
- if (! s_instance)
- {
- pthread_mutex_lock (&s_mutex);
- if (! s_instance)
- s_instance = new Singleton;
- pthread_mutex_unlock (&s_mutex);
- }
- return *s_instance;
- }
- };
- Singleton* Singleton::s_instance = NULL;
- pthread_mutex_t Singleton::s_mutex = PTHREAD_MUTEX_INITIALIZER;_
- int main(int argc, const char * argv[])
- {
- Singleton& a = Singleton::getInstance();
- std::cout << std::hex << &a << std::endl;
- Singleton& b = Singleton::getInstance();
- std::cout << std::hex << &b << std::endl;
- return 0;
- }
上述代码中,以下代码:
- static Singleton& getInstance (void)
- {
- if (! s_instance)
- {
- pthread_mutex_lock (&s_mutex);
- if (! s_instance)
- s_instance = new Singleton;
- pthread_mutex_unlock (&s_mutex);
- }
- return *s_instance;
- }
在获得类Singleton的唯一对象实例的成员函数getInstance中,通过加互斥锁的方法来保证多线程安全。如下代码:
- pthread_mutex_lock (&s_mutex);
首先设置互斥变量s_mutex,此时如果有其它线程已经设置了该变量,则当前线程就等待,直到其他线程释放互斥变量,此线程才重新开始执行。
以下代码:
- pthread_mutex_unlock (&s_mutex);
是释放互斥变量s_mutex。
本案例的完整代码如下所示:
- #include <iostream>
- class Singleton
- {
- private:
- Singleton (void)
- {
- }
- Singleton (Singleton const& that)
- {
- }
- static Singleton* s_instance;
- static pthread_mutex_t s_mutex;
- public:
- static Singleton& getInstance (void)
- {
- if (! s_instance)
- {
- pthread_mutex_lock (&s_mutex);
- if (! s_instance)
- s_instance = new Singleton;
- pthread_mutex_unlock (&s_mutex);
- }
- return *s_instance;
- }
- };
- Singleton* Singleton::s_instance = NULL;
- pthread_mutex_t Singleton::s_mutex = PTHREAD_MUTEX_INITIALIZER;_
- int main(int argc, const char * argv[])
- {
- Singleton& a = Singleton::getInstance();
- std::cout << std::hex << &a << std::endl;
- Singleton& b = Singleton::getInstance();
- std::cout << std::hex << &b << std::endl;
- return 0;
- }
成员变量指针的本质,就是特定成员变量在类对象实例中的相对地址。成员变量指针解引用,就是根据类对象实例的起始地址,结合成员变量指针中的相对地址,计算出具体成员变量的绝对地址,并访问之。类的静态成员变量不是对象的一部分,不需要根据相对地址计算绝对地址,也不需要通过对象或其指针解引用。
成员函数并不存储在对象中,不存在根据相对地址计算绝对地址的问题,但是需要通过对象或对象指针对成员函数指针解引用,其目的只有一个,即提供this指针。类的静态成员函数没有this指针,无需调用对象。
静态成员与对象无关,因此静态成员指针与普通指针并没有任何本质性区别。
实现此案例需要按照如下步骤进行。
步骤一:通过成员变量指针访问成员变量
代码如下:
- #include <iostream>
- class Student
- {
- public:
- std::string m_name;
- std::string m_sex;
- int m_age;
- int m_no;
- };
- int main(int argc, const char * argv[])
- {
- int Student::*p_age = &Student::m_age;
- Student student;
- student.*p_age = 10;
- std::cout << student.m_age << std::endl;
- return 0;
- }
上述代码中,以下代码:
- int Student::*p_age = &Student::m_age;
定义了一个成员变量指针p_age,指向类Student的变量成员m_age.
上述代码中,以下代码:
- Student student;
- student.*p_age = 10;
定义了一个类Student的对象student,然后通过成员变量指针p_age,将对象student中的变量成员m_age赋值为10,这被称为解引用。其实,成员变量指针p_age指向的是变量成员m_age在对象student中的相对地址。“.*”是一个独立的运算符,成员指针解引用运算符。
步骤二:通过成员函数指针访问成员函数
代码如下:
- #include <iostream>
- class Student
- {
- public:
- std::string m_name;
- std::string m_sex;
- int m_age;
- int m_no;
- void who (void) const
- {
- std::cout << "我是" << m_name << ",性别" << m_sex << ",今年" << m_age << "岁,学号" << m_no << std::endl;
- }
- };
- int main(int argc, const char * argv[])
- {
- int Student::*p_age = &Student::m_age;
- Student student;
- student.*p_age = 10;
- std::cout << student.m_age << std::endl;
- student.m_name = "张三";
- student.m_no = 12;
- student.m_sex = "男";
- void (Student::*p_who) (void) const = &Student::who;
- (student.*p_who)();
- return 0;
- }
上述代码中,以下代码:
- void (Student::*p_who) (void) const = &Student::who;
定义了一个成员函数指针p_who,指向类Student的函数成员who。
上述代码中,以下代码:
- (student.*p_who)();
通过成员函数指针p_who,调用类Student的成员函数who,这被称为解引用。添加student的目的只有一个,即提供this指针。
步骤三:通过静态成员指针访问静态成员
代码如下:
- #include <iostream>
- class Student
- {
- public:
- std::string m_name;
- std::string m_sex;
- int m_age;
- int m_no;
- static int s_count;
- void who (void) const
- {
- std::cout << "我是" << m_name << ",性别" << m_sex << ",今年" << m_age << "岁,学号" << m_no << std::endl;
- }
- static void inc (void)
- {
- s_count++;
- }
- };
- int Student::s_count = 0;
- int main(int argc, const char * argv[])
- {
- int Student::*p_age = &Student::m_age;
- Student student;
- student.*p_age = 10;
- std::cout << "age = " << student.m_age << std::endl;
- student.m_name = "张三";
- student.m_no = 12;
- student.m_sex = "男";
- void (Student::*p_who) (void) const = &Student::who;
- (student.*p_who)();
- int* p_count = &Student::s_count;
- *p_count = 10;
- std::cout << "count = " << Student::s_count << std::endl;
- void (*p_inc)(void) = Student::inc;
- (*p_inc)();
- std::cout << "count = " << Student::s_count << std::endl;
- return 0;
- }
上述代码中,以下代码:
- int* p_count = &Student::s_count;
- *p_count = 10;
定义了一个静态成员指针变量p_count,指向类Student的静态变量成员s_count。静态成员指针与普通指针并没有任何本质性区别,只是成员指针受访问控制属性约束。
上述代码中,以下代码:
- void (*p_inc)(void) = Student::inc;
定义了一个静态成员函数指针p_inc,指向类Student的函数成员inc。
上述代码中,以下代码:
- (*p_inc)();
通过静态成员函数指针p_inc,调用类Student的成员函数inc。类的静态成员函数没有this指针,无需调用对象。
本案例的完整代码如下所示:
- #include <iostream>
- class Student
- {
- public:
- std::string m_name;
- std::string m_sex;
- int m_age;
- int m_no;
- static int s_count;
- void who (void) const
- {
- std::cout << "我是" << m_name << ",性别" << m_sex << ",今年" << m_age << "岁,学号" << m_no << std::endl;
- }
- static void inc (void)
- {
- s_count++;
- }
- };
- int Student::s_count = 0;
- int main(int argc, const char * argv[])
- {
- int Student::*p_age = &Student::m_age;
- Student student;
- student.*p_age = 10;
- std::cout << "age = " << student.m_age << std::endl;
- student.m_name = "张三";
- student.m_no = 12;
- student.m_sex = "男";
- void (Student::*p_who) (void) const = &Student::who;
- (student.*p_who)();
- int* p_count = &Student::s_count;
- *p_count = 10;
- std::cout << "count = " << Student::s_count << std::endl;
- void (*p_inc)(void) = Student::inc;
- (*p_inc)();
- std::cout << "count = " << Student::s_count << std::endl;
- return 0;
- }
简单工厂模式
11.1 问题
全部由纯虚函数构成的抽象类称为纯抽象类或接口。面向抽象编程,使得所有基于接口编写的代码,在子类被更替后,无需做任何修改或只需做很少的修改,就能在新子类上正确运行。
11.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:简单工厂模式
代码如下:
- #include <iostream>
- typedef enum ProductTypeTag
- {
- TypeA,
- TypeB,
- TypeC
- }PRODUCTTYPE;
- // Here is the product class
- class Product
- {
- public:
- virtual void show() = 0;
- };
- class ProductA : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductA" << std::endl;
- }
- };
- class ProductB : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductB" << std::endl;
- }
- };
- class ProductC : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductC" << std::endl;
- }
- };
- // Here is the Factory class
- class Factory
- {
- public:
- Product* CreateProduct(PRODUCTTYPE type)
- {
- switch (type)
- {
- case TypeA:
- return new ProductA;
- case TypeB:
- return new ProductB;
- case TypeC:
- return new ProductC;
- default:
- return NULL;
- }
- }
- };
- int main(int argc, char *argv[])
- {
- // First, create a factory object
- Factory *ProductFactory = new Factory();
- Product *productObjA = ProductFactory->CreateProduct(TypeA);
- if (productObjA != NULL)
- productObjA->show();
- Product *productObjB = ProductFactory->CreateProduct(TypeB);
- if (productObjB != NULL)
- productObjB->show();
- Product *productObjC = ProductFactory->CreateProduct(TypeC);
- if (productObjC != NULL)
- productObjC->show();
- delete ProductFactory;
- delete productObjA;
- delete productObjB;
- delete productObjC;
- return 0;
- }
上述代码中,以下代码:
- class Product
- {
- public:
- virtual void show() = 0;
- };
定义了一个抽象产品类Product。
上述代码中,以下代码:
- class ProductA : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductA" << std::endl;
- }
- };
- class ProductB : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductB" << std::endl;
- }
- };
- class ProductC : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductC" << std::endl;
- }
- };
定义了3个具体产品类。在这3个类中,分别对纯虚函数show进行了实现。还可能有更多的具体产品类。这样就造成创建对象时,对象多而杂。
上述代码中,以下代码:
- class Factory
- {
- public:
- Product* CreateProduct(PRODUCTTYPE type)
- {
- switch (type)
- {
- case TypeA:
- return new ProductA;
- case TypeB:
- return new ProductB;
- case TypeC:
- return new ProductC;
- default:
- return NULL;
- }
- }
- };
定义了一个简单工厂类Factory,其中含有一个成员函数CreateProduct用于生成具体产品对象,以简化创建对象时,对象多而杂的现象。这样就能把对象的创建和操作两部分分离开,方便后期的程序扩展和维护。
上述代码中,以下代码:
- Factory *ProductFactory = new Factory();
- Product *productObjA = ProductFactory->CreateProduct(TypeA);
- if (productObjA != NULL)
- productObjA->show();
在主程序中,使用简单工厂类Factory的CreateProduct函数生成具体产品对象,用抽象基类Product的指针实现多态。
11.3 完整代码
本案例的完整代码如下所示:
- #include <iostream>
- typedef enum ProductTypeTag
- {
- TypeA,
- TypeB,
- TypeC
- }PRODUCTTYPE;
- // Here is the product class
- class Product
- {
- public:
- virtual void show() = 0;
- };
- class ProductA : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductA" << std::endl;
- }
- };
- class ProductB : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductB" << std::endl;
- }
- };
- class ProductC : public Product
- {
- public:
- void show(void)
- {
- std::cout << "I'm ProductC" << std::endl;
- }
- };
- // Here is the Factory class
- class Factory
- {
- public:
- Product* CreateProduct(PRODUCTTYPE type)
- {
- switch (type)
- {
- case TypeA:
- return new ProductA;
- case TypeB:
- return new ProductB;
- case TypeC:
- return new ProductC;
- default:
- return NULL;
- }
- }
- };
- int main(int argc, char *argv[])
- {
- // First, create a factory object
- Factory *ProductFactory = new Factory();
- Product *productObjA = ProductFactory->CreateProduct(TypeA);
- if (productObjA != NULL)
- productObjA->show();
- Product *productObjB = ProductFactory->CreateProduct(TypeB);
- if (productObjB != NULL)
- productObjB->show();
- Product *productObjC = ProductFactory->CreateProduct(TypeC);
- if (productObjC != NULL)
- productObjC->show();
- delete ProductFactory;
- delete productObjA;
- delete productObjB;
- delete productObjC;
- return 0;
- }
12 模板方法模式
12.1 问题
全部由纯虚函数构成的抽象类称为纯抽象类或接口。面向抽象编程,使得所有基于接口编写的代码,在子类被更替后,无需做任何修改或只需做很少的修改,就能在新子类上正确运行。
12.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:模板方法模式
代码如下:
- #include <iostream>
- class Abstract
- {
- protected:
- virtual void PrimitiveOperation1() = 0;
- virtual void PrimitiveOperation2() = 0;
- public:
- void TemplateMethod()
- {
- std::cout << "TemplateMethod" << std::endl;
- PrimitiveOperation1();
- PrimitiveOperation2();
- }
- };
- class ConcreteA : public Abstract
- {
- protected:
- virtual void PrimitiveOperation1()
- {
- std::cout << "ConcreteA Operation1" << std::endl;
- }
- virtual void PrimitiveOperation2()
- {
- std::cout << "ConcreteA Operation2" << std::endl;
- }
- };
- class ConcreteB : public Abstract
- {
- protected:
- virtual void PrimitiveOperation1()
- {
- std::cout << "ConcreteB Operation1" << std::endl;
- }
- virtual void PrimitiveOperation2()
- {
- std::cout << "ConcreteB Operation2" << std::endl;
- }
- };
- int main()
- {
- Abstract *pAbstractA = new ConcreteA;
- pAbstractA->TemplateMethod();
- Abstract *pAbstractB = new ConcreteB;
- pAbstractB->TemplateMethod();
- delete pAbstractA;
- delete pAbstractB;
- }
上述代码中,以下代码:
- class Abstract
- {
- protected:
- virtual void PrimitiveOperation1() = 0;
- virtual void PrimitiveOperation2() = 0;
- public:
- void TemplateMethod()
- {
- std::cout << "TemplateMethod" << std::endl;
- PrimitiveOperation1();
- PrimitiveOperation2();
- }
- };
定义了抽象模板类Abstract。在该类中,定义两个抽象的原语操作纯虚函数PrimitiveOperation1和纯虚函数PrimitiveOperation2,用这两个原语操作定义一个抽象算法的两个步骤。在该类中还定义了一个模板成员函数TemplateMethod负责整合两个原语操作及其它一些操作,形成一个不变的算法流程。
上述代码中,以下代码:
- class ConcreteA : public Abstract
- {
- protected:
- virtual void PrimitiveOperation1()
- {
- std::cout << "ConcreteA Operation1" << std::endl;
- }
- virtual void PrimitiveOperation2()
- {
- std::cout << "ConcreteA Operation2" << std::endl;
- }
- };
- class ConcreteB : public Abstract
- {
- protected:
- virtual void PrimitiveOperation1()
- {
- std::cout << "ConcreteB Operation1" << std::endl;
- }
- virtual void PrimitiveOperation2()
- {
- std::cout << "ConcreteB Operation2" << std::endl;
- }
- };
定义了两个实体类,在这两个实体类中,各自实现两个具体的原语操作,完成各自实体中的具体算法的各步骤的具体操作。这样如果再有其它的具体算法,只需要再定义一个实体类,实现具体的原语操作即可。
上述代码中,以下代码:
- Abstract *pAbstractA = new ConcreteA;
- pAbstractA->TemplateMethod();
在主程序中,用抽象模板类Abstract的指针指向实体类ConcreteA,完成一个具体的算法。
12.3 完整代码
本案例的完整代码如下所示:
- #include <iostream>
- class Abstract
- {
- protected:
- virtual void PrimitiveOperation1() = 0;
- virtual void PrimitiveOperation2() = 0;
- public:
- void TemplateMethod()
- {
- std::cout << "TemplateMethod" << std::endl;
- PrimitiveOperation1();
- PrimitiveOperation2();
- }
- };
- class ConcreteA : public Abstract
- {
- protected:
- virtual void PrimitiveOperation1()
- {
- std::cout << "ConcreteA Operation1" << std::endl;
- }
- virtual void PrimitiveOperation2()
- {
- std::cout << "ConcreteA Operation2" << std::endl;
- }
- };
- class ConcreteB : public Abstract
- {
- protected:
- virtual void PrimitiveOperation1()
- {
- std::cout << "ConcreteB Operation1" << std::endl;
- }
- virtual void PrimitiveOperation2()
- {
- std::cout << "ConcreteB Operation2" << std::endl;
- }
- };
- int main()
- {
- Abstract *pAbstractA = new ConcreteA;
- pAbstractA->TemplateMethod();
- Abstract *pAbstractB = new ConcreteB;
- pAbstractB->TemplateMethod();
- delete pAbstractA;
- delete pAbstractB;
- }
13 单继承虚表模型
13.1 问题
编译器会为每个包含虚函数的类生成一张虚函数表,即存放每个虚函数地址的函数指针数组,简称虚表(vtbl)。
13.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:单继承虚表模型
代码如下:
- #include <iostream>
- class Base
- {
- public:
- virtual int f1 (void)
- {
- std::cout << "Base f1" << std::endl;
- return 0;
- }
- virtual void f2 (int i)
- {
- std::cout << "Base f2" << std::endl;
- }
- virtual int f3 (int i)
- {
- std::cout << "Base f3" << std::endl;
- return 0;
- }
- };
- class Derived : public Base
- {
- public:
- int f1 (void)
- {
- std::cout << "Derived f1" << std::endl;
- return 0;
- }
- int f3 (int i)
- {
- std::cout << "Derived f3" << std::endl;
- return 0;
- }
- virtual void f4 (void)
- {
- std::cout << "Derived f4" << std::endl;
- }
- };
- int main()
- {
- Base *b = new Base;
- b->f3(10);
- delete b;
- b = new Derived;
- b->f3(20);
- delete b;
- }
上述代码中,以下代码:
- class Base
- {
- public:
- virtual int f1 (void)
- {
- std::cout << "Base f1" << std::endl;
- return 0;
- }
- virtual void f2 (int i)
- {
- std::cout << "Base f2" << std::endl;
- }
- virtual int f3 (int i)
- {
- std::cout << "Base f3" << std::endl;
- return 0;
- }
- };
定义了一个类Base,该类中含有三个虚函数。编译器会为类Base生成一张虚函数表,该表是用于存放每个虚函数地址的函数指针数组,简称虚表(vtbl),每个虚函数对应一个虚函数表中的数组元素。即vtbl[0]->f1,vtbl[1]->f2,vtbl[2]->f3。
除了为类Base生成虚函数表以外,编译器还会为类Base增加一个隐式的成员变量,通常在该类实例化对象的起始位置,用于存放虚函数表的首地址,该变量被称为虚函数表指针,简称虚指针(vptr)。即vptr = &vtbl[0]。
这样,主程序中以下语句:
- Base *b = new Base;
- b->f3(10);
相当于:
- Base *b = new Base;
- b->vptr[2](10);
虚表是一个类一张,而不是一个对象一张,同一个类的多个对象,通过各自的虚指针,共享同一张虚表。
上述代码中,以下代码:
- class Derived : public Base
- {
- public:
- int f1 (void)
- {
- std::cout << "Derived f1" << std::endl;
- return 0;
- }
- int f3 (int i)
- {
- std::cout << "Derived f3" << std::endl;
- return 0;
- }
- virtual void f4 (void)
- {
- std::cout << "Derived f4" << std::endl;
- }
- };
定义了子类Derived,并且在子类Derived中覆盖了基类Base的f1和f3,继承了基类Base的f2,增加了自己的f4,编译器同样会为子类生成一张专属于它的虚表。指向子类Derived虚表的虚指针就存放在子类对象的基类子对象中,通常还是在起始位置。
这样,主程序中以下语句:
- b = new Derived;
- b->f3(20);
相当于:
- b = new Derived;
- b->vptr[2](20);
此时vptr[2]中存放的是子类Derived中覆盖基类Base的f3,而这就是所谓的多态。
13.3 完整代码
本案例的完整代码如下所示:
- #include <iostream>
- class Base
- {
- public:
- virtual int f1 (void)
- {
- std::cout << "Base f1" << std::endl;
- return 0;
- }
- virtual void f2 (int i)
- {
- std::cout << "Base f2" << std::endl;
- }
- virtual int f3 (int i)
- {
- std::cout << "Base f3" << std::endl;
- return 0;
- }
- };
- class Derived : public Base
- {
- public:
- int f1 (void)
- {
- std::cout << "Derived f1" << std::endl;
- return 0;
- }
- int f3 (int i)
- {
- std::cout << "Derived f3" << std::endl;
- return 0;
- }
- virtual void f4 (void)
- {
- std::cout << "Derived f4" << std::endl;
- }
- };
- int main()
- {
- Base *b = new Base;
- b->f3(10);
- delete b;
- b = new Derived;
- b->f3(20);
- delete b;
- }
14 动态类型转换
14.1 问题
动态类型转换(dynamic_cast)用于将基类类型的指针或引用转换为其子类类型的指针或引用,前提是子类必须从基类多态继承,即基类包含至少一个虚函数
14.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:动态类型转换
代码如下:
- #include <iostream>
- class Base
- {
- public:
- virtual int f1 (void)
- {
- std::cout << "Base f1" << std::endl;
- return 0;
- }
- };
- class Derived : public Base
- {
- public:
- int f1 (void)
- {
- std::cout << "Derived f1" << std::endl;
- return 0;
- }
- };
- int main()
- {
- Derived d;
- Base* pa = &d;
- Derived* pb = dynamic_cast<Derived*> (pa);
- if (pb == NULL)
- std::cout << "转换失败!" << std::endl;
- Base& ra = d;
- try {
- Derived& rb = dynamic_cast<Derived&> (ra);
- }
- catch (std::bad_cast& ex)
- {
- std::cout << "转换失败!" << std::endl;
- }
- Derived b = dynamic_cast<Derived> (*pa);
- return 0;
- }
上述代码中,以下代码:
- Derived d;
- Base* pa = &d;
- Derived* pb = dynamic_cast<Derived*> (pa);
应用动态类型转换,将基类Base类型的指针转换为其子类Derived类型的指针。
上述代码中,以下代码:
- if (pb == NULL)
- std::cout << "转换失败!" << std::endl;
针对指针的动态类型转换,以返回空指针(NULL)表示失败。
上述代码中,以下代码:
- Base& ra = d;
- try {
- Derived& rb = dynamic_cast<Derived&> (ra);
- }
应用动态类型转换,将基类Base类型的引用转换为其子类Derived类型的引用。
上述代码中,以下代码:
- Base& ra = d;
- try {
- Derived& rb = dynamic_cast<Derived&> (ra);
- }
- catch (std::bad_cast& ex)
- {
- std::cout << "转换失败!" << std::endl;
- }
针对引用的动态类型转换,以抛出bad_cast异常表示失败。
上述代码中,以下代码:
- Derived b = dynamic_cast<Derived> (*pa);
不是针对指针或引用做的动态类型转换,编译错误。另外,要注意,转换目标类型和源类型之间不具有多态继承性也会发生编译错误。转换源类型的目标对象非目标类型会发生运行错误。
14.3 完整代码
本案例的完整代码如下所示:
- #include <iostream>
- class Base
- {
- public:
- virtual int f1 (void)
- {
- std::cout << "Base f1" << std::endl;
- return 0;
- }
- };
- class Derived : public Base
- {
- public:
- int f1 (void)
- {
- std::cout << "Derived f1" << std::endl;
- return 0;
- }
- };
- int main()
- {
- Derived d;
- Base* pa = &d;
- Derived* pb = dynamic_cast<Derived*> (pa);
- if (pb == NULL)
- std::cout << "转换失败!" << std::endl;
- Base& ra = d;
- try {
- Derived& rb = dynamic_cast<Derived&> (ra);
- }
- catch (std::bad_cast& ex)
- {
- std::cout << "转换失败!" << std::endl;
- }
- Derived b = dynamic_cast<Derived> (*pa);
- return 0;
- }
15 类型信息
15.1 问题
typeid操作符既可用于类型也可用于对象,用于返回类型信息,即类typeinfo对象的常引用。类typeinfo中含所有一个成员函数name,通过它可以得到类型名。
15.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:类型信息
代码如下:
- #include <iostream>
- class A
- {
- };
- class B : public A
- {
- public:
- virtual void foo (void)
- {
- }
- };
- class C : public B
- {
- };
- int main()
- {
- int x;
- int * px;
- std::cout << "int is: " << typeid(int).name() << std::endl;
- std::cout << " x is: " << typeid(x).name() << std::endl;
- std::cout << " px is: " << typeid(px).name() << std::endl;
- std::cout << "*px is: " << typeid(*px).name() << std::endl;
- C c;
- A& a = c;
- std::cout << typeid (a).name () << std::endl;
- B& b = c;
- std::cout << typeid (b).name () << std::endl;
- return 0;
- }
上述代码中,以下代码:
- int x;
- int * px;
- std::cout << "int is: " << typeid(int).name() << std::endl;
- std::cout << " x is: " << typeid(x).name() << std::endl;
- std::cout << " px is: " << typeid(px).name() << std::endl;
- std::cout << "*px is: " << typeid(*px).name() << std::endl;
使用typeid获取指定目标的类型信息。如:
- std::cout << "int is: " << typeid(int).name() << std::endl;
的输出结果为:int is: i。
上述代码中,以下代码:
- C c;
- A& a = c;
- std::cout << typeid (a).name () << std::endl;
当typeid作用于基类A类型的引用的目标c时,若基类A中不包含任何虚函数,则该操作符所返回的类型信息由引用A本身的类型决定,即返回的是类A的类名A。
上述代码中,以下代码:
- B& b = c;
- std::cout << typeid (b).name () << std::endl;
当typeid作用于基类B类型的引用的目标c时,若基类包含至少一个虚函数,即存在多态继承,该操作符所返回的类型信息由该引用的实际目标对象的类型决定,即返回的是类C的类名C。
15.3 完整代码
本案例的完整代码如下所示:
- #include <iostream>
- class A
- {
- };
- class B : public A
- {
- public:
- virtual void foo (void)
- {
- }
- };
- class C : public B
- {
- };
- int main()
- {
- int x;
- int * px;
- std::cout << "int is: " << typeid(int).name() << std::endl;
- std::cout << " x is: " << typeid(x).name() << std::endl;
- std::cout << " px is: " << typeid(px).name() << std::endl;
- std::cout << "*px is: " << typeid(*px).name() << std::endl;
- C c;
- A& a = c;
- std::cout << typeid (a).name () << std::endl;
- B& b = c;
- std::cout << typeid (b).name () << std::endl;
- return 0;
- }