您的位置:首页 > 编程语言 > C语言/C++

C++002常见设计模式

2016-04-22 14:53 162 查看

饿汉式的单例模式

1.1 问题

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

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

1.2 步骤

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

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

代码如下:

#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的唯一对象实例。

1.3 完整代码

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

#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;
}


2 懒汉式的单例模式

2.1 问题

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

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

2.2 步骤

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

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

代码如下:

#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第一次被调用时,才创建唯一对象实例,而不是在创建程序一开始就创建唯一对象实例。

2.3 完整代码

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

#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;
}


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

3.1 问题

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

3.2 步骤

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

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

代码如下:

#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。

3.3 完整代码

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

#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;
}


4 通过成员指针访问对象

4.1 问题

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

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

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

4.2 步骤

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

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

代码如下:

#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指针,无需调用对象。

4.3 完整代码

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

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: