我们可以在C++中使类构造函数虚拟化以创建多态对象吗?不会。C++是静态类型的(RTTI的目的有所不同)语言,对于C++编译器来说,多态创建对象是没有意义的。编译器必须知道创建对象的类类型。换句话说,从C++编译器的角度来看,要创建的对象类型是编译时的决定。如果我们将构造函数设为虚拟,则编译器会标记错误。实际上,除了inline之外,构造函数的声明中不允许其他关键字。
在实际情况下,我们需要基于一些输入在类层次结构中创建派生类对象。换句话说,对象创建和对象类型紧密耦合,这迫使修改扩展。虚拟构造器的目的是使对象创建与类型脱钩。
我们如何在运行时创建所需的对象类型?例如,请参见下面的示例程序。
#include
using namespace std;
//// LIBRARY START
class Base
{
public:
Base() { }
virtual // Ensures to invoke actual object destructor
~Base() { }
// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base
{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}
~Derived1()
{
cout << "Derived1 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};
class Derived2 : public Base
{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}
~Derived2()
{
cout << "Derived2 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};
//// LIBRARY END
class User
{
public:
// Creates Drived1
User() : pBase(nullptr)
{
// What if Derived2 is required? - Add an if-else ladder (see next sample)
pBase = new Derived1();
}
~User()
{
if( pBase )
{
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action()
{
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main()
{
User *user = new User();
// Need Derived1 functionality only
user->Action();
delete user;
}
在以上示例中,假定层次结构Base , Derived1和Derived2是库代码的一部分。 User类是试图利用层次结构的实用程序类。主要函数是通过User类使用基本层次结构功能。
User类构造函数始终在创建Derived1对象。如果用户的使用者(在我们的例子中是主要用户)需要Derived2功能,则用户需要创建“ new Derived2() ”并强制重新编译。重新编译是不好的设计方法,因此我们可以选择以下方法。
在详细介绍之前,让我们回答一下,谁将指示创建Derived1或Derived2对象?显然,它是User类的使用者。 User类可以使用if-else阶梯创建Derived1或Derived2 ,如以下示例所示,
#include
using namespace std;
//// LIBRARY START
class Base
{
public:
Base() { }
virtual // Ensures to invoke actual object destructor
~Base() { }
// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base
{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}
~Derived1()
{
cout << "Derived1 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};
class Derived2 : public Base
{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}
~Derived2()
{
cout << "Derived2 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};
//// LIBRARY END
class User
{
public:
// Creates Derived1 or Derived2 based on input
User() : pBase(nullptr)
{
int input; // ID to distinguish between
// Derived1 and Derived2
cout << "Enter ID (1 or 2): ";
cin >> input;
while( (input != 1) && (input != 2) )
{
cout << "Enter ID (1 or 2 only): ";
cin >> input;
}
if( input == 1 )
{
pBase = new Derived1;
}
else
{
pBase = new Derived2;
}
// What if Derived3 being added to the class hierarchy?
}
~User()
{
if( pBase )
{
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action()
{
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main()
{
User *user = new User();
// Need either Derived1 or Derived2 functionality
user->Action();
delete user;
}
上面的代码*不*开放扩展,这是一种不灵活的设计。简而言之,如果库使用新类Derived3更新基类层次结构。 User类如何创建Derived3对象?一种方法是更新基于新输入ID 3来创建Derived3对象的if-else阶梯,如下所示,
#include
using namespace std;
class User
{
public:
User() : pBase(nullptr)
{
// Creates Drived1 or Derived2 based on need
int input; // ID to distinguish between
// Derived1 and Derived2
cout << "Enter ID (1 or 2): ";
cin >> input;
while( (input != 1) && (input != 2) )
{
cout << "Enter ID (1 or 2 only): ";
cin >> input;
}
if( input == 1 )
{
pBase = new Derived1;
}
else if( input == 2 )
{
pBase = new Derived2;
}
else
{
pBase = new Derived3;
}
}
~User()
{
if( pBase )
{
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action()
{
pBase->DisplayAction();
}
private:
Base *pBase;
};
上面的修改迫使User类的用户重新编译,不好的(不灵活的)设计!并且不会因基础扩展而关闭用户类的进一步修改。
问题在于对象的创建。将新类添加到层次结构中,从而强制重新编译User类的依赖项。我们不能将创建对象的操作委托给类层次结构本身或虚拟行为的函数吗?通过将对象创建委托给类层次结构(或静态函数),我们可以避免User和Base层次结构之间的紧密耦合。理论足够,请参见以下代码,
#include
using namespace std;
//// LIBRARY START
class Base
{
public:
// The "Virtual Constructor"
static Base *Create(int id);
Base() { }
virtual // Ensures to invoke actual object destructor
~Base() { }
// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base
{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}
~Derived1()
{
cout << "Derived1 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};
class Derived2 : public Base
{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}
~Derived2()
{
cout << "Derived2 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};
class Derived3 : public Base
{
public:
Derived3()
{
cout << "Derived3 created" << endl;
}
~Derived3()
{
cout << "Derived3 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived3" << endl;
}
};
// We can also declare "Create" outside Base
// But it is more relevant to limit it's scope to Base
Base *Base::Create(int id)
{
// Just expand the if-else ladder, if new Derived class is created
// User code need not be recompiled to create newly added class objects
if( id == 1 )
{
return new Derived1;
}
else if( id == 2 )
{
return new Derived2;
}
else
{
return new Derived3;
}
}
//// LIBRARY END
//// UTILITY START
class User
{
public:
User() : pBase(nullptr)
{
// Receives an object of Base hierarchy at runtime
int input;
cout << "Enter ID (1, 2 or 3): ";
cin >> input;
while( (input != 1) && (input != 2) && (input != 3) )
{
cout << "Enter ID (1, 2 or 3 only): ";
cin >> input;
}
// Get object from the "Virtual Constructor"
pBase = Base::Create(input);
}
~User()
{
if( pBase )
{
delete pBase;
pBase = nullptr;
}
}
// Delegates to actual object
void Action()
{
pBase->DisplayAction();
}
private:
Base *pBase;
};
//// UTILITY END
//// Consumer of User (UTILITY) class
int main()
{
User *user = new User();
// Action required on any of Derived objects
user->Action();
delete user;
}
User类独立于对象创建。它将责任委托给Base ,并以ID的形式提供输入。如果库添加了新的类Derived4 ,则库修改器将扩展Create内的if-else梯形图以返回正确的对象。由于Base的扩展, User的使用者不需要重新编译其代码。
请注意,函数Create用于在运行时返回不同类型的基类对象。它的作用类似于虚拟构造函数,在模式术语中也称为Factory Method 。
模式世界展示了实现上述概念的不同方法。此外,以上代码还存在一些潜在的设计问题。我们的目标是提供一些有关虚拟构造的见解,并根据一些输入动态创建对象。我们有专门针对该主题的书籍,有兴趣的读者可以参考它们以获取更多信息。