Can we make a class constructor virtual in C++ to create polymorphic objects?

  No. C++ being static typed (the purpose of RTTI is different) language, it is meaningless to the C++ compiler to create an object polymorphically. The compiler must be aware of the class type to create the object.

  In other words, what type of object to be created is a compile time decision from C++ compiler perspective. If we make constructor virtual, compiler flags an error. In fact except inline, no other keyword is allowed in the declaration of constructor.

  In practical scenarios(情节) we would need to create a derived class object in a class hierarchy based on some input. Putting in other words, object creation and object type are tightly coupled which forces modifications to extended. The objective of virtual constructor is to decouple object creation from it’s type.

  How can we create required type of object at runtime? For example, see the following sample program.

 1 #include <iostream>
2 using namespace std;
3
4 //// LIBRARY START
5 class Base
6 {
7 public:
8 Base()
9 {
10 }
11 virtual ~Base() // Ensures to invoke actual object destructor
12 {
13 }
14
15 // An interface
16 virtual void DisplayAction() = 0;
17 };
18
19 class Derived1 : public Base
20 {
21 public:
22 Derived1()
23 {
24 cout << "Derived1 created" << endl;
25 }
26
27 ~Derived1()
28 {
29 cout << "Derived1 destroyed" << endl;
30 }
31
32 void DisplayAction()
33 {
34 cout << "Action from Derived1" << endl;
35 }
36 };
37
38 class Derived2 : public Base
39 {
40 public:
41 Derived2()
42 {
43 cout << "Derived2 created" << endl;
44 }
45
46 ~Derived2()
47 {
48 cout << "Derived2 destroyed" << endl;
49 }
50
51 void DisplayAction()
52 {
53 cout << "Action from Derived2" << endl;
54 }
55 };
56
57 //// LIBRARY END
58
59 class User
60 {
61 public:
62
63 // Creates Drived1
64 User() : pBase(0)
65 {
66 // What if Derived2 is required? - Add an if-else ladder (see next sample)
67 pBase = new Derived1();
68 }
69
70 ~User()
71 {
72 if( pBase )
73 {
74 delete pBase;
75 pBase = 0;
76 }
77 }
78
79 // Delegates to actual object
80 void Action()
81 {
82 pBase->DisplayAction();
83 }
84
85 private:
86 Base *pBase;
87 };
88
89 int main()
90 {
91 User *user = new User();
92
93 // Need Derived1 functionality only
94 user->Action();
95
96 delete user;
97 }

  Output:

  Derived1 created
  Action from Derived1
  Derived1 destroyed

  In the above sample, assume that the hierarchy Base, Derived1 and Derived2 are part of library code. The class User is utility class trying to make use of the hierarchy. The main function is consuming Base hierarchy functionality via User class.

  The User class constructor is creating Derived1 object, always. If the User‘s consumer (the main in our case) needs Derived2 functionality, User needs to create "new Derived2()" and it forces recompilation. Recompiling is bad way of design, so we can opt for the following approach.

  Before going into details, let us answer, who will dictate to create either of Derived1 or Derived2 object? Clearly, it is the consumer of User class. The User class can make use of if-else ladder to create either Derived1 or Derived2, as shown in the following sample,

  1 #include <iostream>
2 using namespace std;
3
4 //// LIBRARY START
5 class Base
6 {
7 public:
8 Base() { }
9
10 virtual // Ensures to invoke actual object destructor
11 ~Base() { }
12
13 // An interface
14 virtual void DisplayAction() = 0;
15 };
16
17 class Derived1 : public Base
18 {
19 public:
20 Derived1()
21 {
22 cout << "Derived1 created" << endl;
23 }
24
25 ~Derived1()
26 {
27 cout << "Derived1 destroyed" << endl;
28 }
29
30 void DisplayAction()
31 {
32 cout << "Action from Derived1" << endl;
33 }
34 };
35
36 class Derived2 : public Base
37 {
38 public:
39 Derived2()
40 {
41 cout << "Derived2 created" << endl;
42 }
43
44 ~Derived2()
45 {
46 cout << "Derived2 destroyed" << endl;
47 }
48
49 void DisplayAction()
50 {
51 cout << "Action from Derived2" << endl;
52 }
53 };
54
55 //// LIBRARY END
56
57 class User
58 {
59 public:
60
61 // Creates Derived1 or Derived2 based on input
62 User() : pBase(0)
63 {
64 int input; // ID to distinguish between
65 // Derived1 and Derived2
66
67 cout << "Enter ID (1 or 2): ";
68 cin >> input;
69
70 while( (input != 1) && (input != 2) )
71 {
72 cout << "Enter ID (1 or 2 only): ";
73 cin >> input;
74 }
75
76 if( input == 1 )
77 {
78 pBase = new Derived1;
79 }
80 else
81 {
82 pBase = new Derived2;
83 }
84
85 // What if Derived3 being added to the class hierarchy?
86 }
87
88 ~User()
89 {
90 if( pBase )
91 {
92 delete pBase;
93 pBase = 0;
94 }
95 }
96
97 // Delegates to actual object
98 void Action()
99 {
100 pBase->DisplayAction();
101 }
102
103 private:
104 Base *pBase;
105 };
106
107 int main()
108 {
109 User *user = new User();
110
111 // Need either Derived1 or Derived2 functionality
112 user->Action();
113
114 delete user;
115 }

  The above code is *not* open for extension, an inflexible design. In simple words, if the library updates the Base class hierarchy with new class Derived3. How can the User class creates Derived3 object? One way is to update the if-else ladder that creates Derived3 object based on new input ID 3 as shown below.

 1 #include <iostream>
2 using namespace std;
3
4 class User
5 {
6 public:
7 User() : pBase(0)
8 {
9 // Creates Drived1 or Derived2 based on need
10
11 int input; // ID to distinguish between
12 // Derived1 and Derived2
13
14 cout << "Enter ID (1 or 2): ";
15 cin >> input;
16
17 while( (input != 1) && (input != 2) )
18 {
19 cout << "Enter ID (1 or 2 only): ";
20 cin >> input;
21 }
22
23 if( input == 1 )
24 {
25 pBase = new Derived1;
26 }
27 else if( input == 2 )
28 {
29 pBase = new Derived2;
30 }
31 else
32 {
33 pBase = new Derived3;
34 }
35 }
36
37 ~User()
38 {
39 if( pBase )
40 {
41 delete pBase;
42 pBase = 0;
43 }
44 }
45
46 // Delegates to actual object
47 void Action()
48 {
49 pBase->DisplayAction();
50 }
51
52 private:
53 Base *pBase;
54 };

  The above modification forces the users of User class to recompile, bad (inflexible) design! And won’t close User class from further modifications due to Base extension.

  The problem is with the creation of objects. Addition of new class to the hierarchy forcing dependents of User class to recompile. Can’t we delegate(代表) the action of creating objects to class hierarchy itself or to a function that behaves virtually? By delegating the object creation to class hierarchy (or to a static function) we can avoid the tight coupling between User and Base hierarchy.

  Enough theory, see the following code,

  1 #include <iostream>
2 using namespace std;
3
4 //// LIBRARY START
5 class Base
6 {
7 public:
8
9 // The "Virtual Constructor"
10 static Base *Create(int id);
11
12 Base()
13 {
14 }
15
16 virtual ~Base() // Ensures to invoke actual object destructor
17 {
18 }
19
20 // An interface
21 virtual void DisplayAction() = 0;
22 };
23
24 class Derived1 : public Base
25 {
26 public:
27 Derived1()
28 {
29 cout << "Derived1 created" << endl;
30 }
31
32 ~Derived1()
33 {
34 cout << "Derived1 destroyed" << endl;
35 }
36
37 void DisplayAction()
38 {
39 cout << "Action from Derived1" << endl;
40 }
41 };
42
43 class Derived2 : public Base
44 {
45 public:
46 Derived2()
47 {
48 cout << "Derived2 created" << endl;
49 }
50
51 ~Derived2()
52 {
53 cout << "Derived2 destroyed" << endl;
54 }
55
56 void DisplayAction()
57 {
58 cout << "Action from Derived2" << endl;
59 }
60 };
61
62 class Derived3 : public Base
63 {
64 public:
65 Derived3()
66 {
67 cout << "Derived3 created" << endl;
68 }
69
70 ~Derived3()
71 {
72 cout << "Derived3 destroyed" << endl;
73 }
74
75 void DisplayAction()
76 {
77 cout << "Action from Derived3" << endl;
78 }
79 };
80
81 // We can also declare "Create" outside Base
82 // But it is more relevant to limit it's scope to Base
83 Base *Base::Create(int id)
84 {
85 // Just expand the if-else ladder, if new Derived class is created
86 // User code need not be recompiled to create newly added class objects
87
88 if( id == 1 )
89 {
90 return new Derived1;
91 }
92 else if( id == 2 )
93 {
94 return new Derived2;
95 }
96 else
97 {
98 return new Derived3;
99 }
100 }
101 //// LIBRARY END
102
103 //// UTILITY START
104 class User
105 {
106 public:
107 User() : pBase(0)
108 {
109 // Receives an object of Base heirarchy at runtime
110
111 int input;
112
113 cout << "Enter ID (1, 2 or 3): ";
114 cin >> input;
115
116 while( (input != 1) && (input != 2) && (input != 3) )
117 {
118 cout << "Enter ID (1, 2 or 3 only): ";
119 cin >> input;
120 }
121
122 // Get object from the "Virtual Constructor"
123 pBase = Base::Create(input);
124 }
125
126 ~User()
127 {
128 if( pBase )
129 {
130 delete pBase;
131 pBase = 0;
132 }
133 }
134
135 // Delegates to actual object
136 void Action()
137 {
138 pBase->DisplayAction();
139 }
140
141 private:
142 Base *pBase;
143 };
144
145 //// UTILITY END
146
147 //// Consumer of User (UTILITY) class
148 int main()
149 {
150 User *user = new User();
151
152 // Action required on any of Derived objects
153 user->Action();
154
155 delete user;
156 }

  The User class is independent of object creation. It delegates that responsibility to Base, and provides an input in the form of ID. If the library adds new class Derived4, the library modifier will extend the if-else ladder inside Create to return proper object. Consumers of User need not recompile their code due to extension of Base.

  Note that the function Create used to return different types of Base class objects at runtime. It acts like virtual constructor, also referred as Factory Method in pattern terminology.

  Pattern world demonstrate different ways to implement the above concept. Also there are some potential design issues with the above code. Our objective is to provide some insights into virtual construction, creating objects dynamically based on some input. We have excellent books devoted to the subject, interested reader can refer them for more information.

  这篇文章讲述的是有设计模式中的工厂模式Factory Pattern,这个我不太懂哦。

  Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

  转载请注明:http://www.cnblogs.com/iloveyouforever/

  2013-11-26  21:00:57

Advanced C++ | Virtual Constructor的更多相关文章

  1. Java设计模式-工厂方法模式(Virtual Constructor/Polymorphic Factory)

    工厂方法模式(Virtual Constructor/Polymorphic Factory) 工厂方法模式是类的创建模式,又叫做虚拟构造子模式(Virtual Constructor)或者多态性工厂 ...

  2. Advanced C++ | Virtual Copy Constructor

    这个不懂,等看会了再写...

  3. Objective-C设计模式——工厂方法模式virtual constructor(对象创建)

    工厂方法模式 工厂方法模式可以控制对象的创建过程,屏蔽对象创建的细节,可以直接创建出我们所需要的已经配置好的对象. 工厂方法模式定义了创建方法的接口,让子类决定实例化哪一个类,工厂方法模式使得一个类的 ...

  4. 【java设计模式】【创建模式Creational Pattern】工厂方法模式Factory Method Pattern(多态性工厂模式Polymorphic Factory Pattern、虚拟构造子模式Virtual Constructor Pattern)

    public class Test { public static void main(String[] args){ Creator ca=new ConcreteCreatorA(); ca.cr ...

  5. 为什么内联函数,构造函数,静态成员函数不能为virtual函数

    http://blog.csdn.net/freeboy1015/article/details/7635012 为什么内联函数,构造函数,静态成员函数不能为virtual函数? 1> 内联函数 ...

  6. Virtual Table

    C++对象模型——吴泰 C/C++杂记 C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现 C++ 多继承和虚继承的内存布局 [已翻译100%] (虚继承参考,推荐) 图说C++对象模型:对 ...

  7. MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化

    1.virtual constructor 在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所 ...

  8. 工厂方法模式——创建型模式02

    1. 简单工厂模式     在介绍工厂方法模式之前,先介绍一下简单工厂模式.虽然简单工厂模式不属于GoF 23种设计模式,但通常将它作为学习其他工厂模式的入门,并且在实际开发中使用的也较为频繁. (1 ...

  9. C++异常处理:try,catch,throw,finally的用法

    写在前面 所谓异常处理,即让一个程序运行时遇到自己无法处理的错误时抛出一个异常,希望调用者可以发现处理问题. 异常处理的基本思想是简化程序的错误代码,为程序键壮性提供一个标准检测机制. 也许我们已经使 ...

随机推荐

  1. ELK集群之kafka(7)

    原理待补充: kafka依赖于zookeeper集群. 都是基于java 由于源码安装jdk 未声明bin下java 在各自server配置文件中声明 JAVA_HOME=/usr/local/jdk ...

  2. 纯前端实现词云展示+附微博热搜词云Demo代码

    前言 最近工作中做了几个数据可视化大屏项目,其中也有用到了词云展示,以前做词云都是用python库来生成图片显示的,这次用了纯前端的实现(Ctrl+V真好用),同时顺手做个微博热搜的词云然后记录一下~ ...

  3. ES6-正则新增(复习+学习)

    ES6-正则 昨天,复习了正则的基本知识,今天学习ES6新增的正则的知识,做一个总结笔记,大家可以先看4,5对应的方法然后再从头看,话不多说直接上: 1.RegExp构造函数的区别 2.新增的修饰符 ...

  4. AsExpandable EF多条件查询

    我个人学习新技术有一个方法,如果遇到问题会根据以前的经验来寻找一些类似的解决方法.有人会说,如果这个问题在你的学习或者工作生涯中都没有遇到过呢?很简单,通过搜索资料或查阅相关书籍学习别人的经验. 在如 ...

  5. Django 小实例S1 简易学生选课管理系统 9 创建课程模型(model)

    Django 小实例S1 简易学生选课管理系统 第9节--创建课程模型(model) 点击查看教程总目录 作者自我介绍:b站小UP主,时常直播编程+红警三,python1对1辅导老师. 对于课程模块, ...

  6. jenkins-发送allure邮件测试报告

    1.安装插件 allure-jenkins-plugin  2.在全局工具配置中,配置allure命令行 3.全局工具配置中,添加JDK配置 4.配置slave节点工具(JDK+Allure)-配置在 ...

  7. 【jmeter学习】Concurrency Thread Group阶梯式加压测试

    安装步骤 1.配置Concurrency Thread Group线程组 下载jmeter插件管理:https://jmeter-plugins.org/install/Install/ 2.配置插件 ...

  8. SQL语句修改字段类型与第一次SQLServer试验解答

    SQL语句修改字段类型 mysql中 alert table name modify column name type; 例子:修改user表中的name属性类型为varchar(50) alert ...

  9. [cf1491F]Magnets

    首先,只需要找到一个有磁性的位置,就可以通过$n-1$次判断其余磁铁是否有磁性,因此也就是要在$\lfloor\log_{2}n\rfloor+1$次中找到一个有磁性的位置 有一个$n-1$次的做法, ...

  10. [atAGC029F]Construction of a tree

    构造一张二分图,左边是$n$个点,右边是$n-1$个集合,按照点属于集合连边 定义一组匹配的意义,即说明该点的父亲在该集合中选择 利用dinic求出二分图的最大匹配,若不为$n-1$则无解,否则考虑如 ...