C++ 如何根据参数的类型实例化不同的类?
我有一个名为Type的虚拟类和派生类Type1和Type2C++ 如何根据参数的类型实例化不同的类?,c++,oop,inheritance,C++,Oop,Inheritance,我有一个名为Type的虚拟类和派生类Type1和Type2 class Type { public: virtual void print() = 0; }; class Type1 : public Type { public: void print() { cout << "I am of type 1" << endl; } }; class Type2 : public Type { public: void print() { cout
class Type {
public:
virtual void print() = 0;
};
class Type1 : public Type {
public:
void print() { cout << "I am of type 1" << endl; }
};
class Type2 : public Type {
public:
void print() { cout << "I am of type 2" << endl; }
};
但根据t的类型调用不同的打印方法。这是否可能(以及如何实现)
编辑 在阅读了评论之后,有两种主要的可能设计:
编辑2
在一些评论中提到的工厂模式呢?有人能告诉我它是否能解决我的问题吗 您的代码工作正常,设计良好。正如评论中所说,“您将功能放在
Type1
/Type2
中,并使用多态性来区分实现”,这是一种很好的方法。您可以遵循这种方法,在
Type1
/Type2
中执行,并以这种方式控制操作中的输出
动态播放
不管第一点是什么,你都可以通过检查类型来实现你想要的。您要检查Action
的构造函数中的t
是否为Type1
类型或Type2
类型。只需尝试使用dynamic\u cast
向下播放即可
Action::print()
的代码可能与此类似:
Action::print(){
Type1* t1 = dynamic_cast<Type1*>(t); /* returns nullptr if t is not of type Type1
/* Check if we got the nullptr or not */
if(t1){
std::cout << "I am an action. My type says: Type 1!" << std::endl;
}
else{
std::cout << "I am an action. My type says: Type 2!" << std::endl;
}
}
输出:
I am an action. My type says: Type 1!
I am an action. My type says: Type 2!
请不要说这是一个最小的例子,我认为使用清晰结构化多态性的解决方案是更好的选择。虽然基于继承的多态性确实有它的用途,但这听起来不像是其中之一。通常,在基于继承的设计中,执行的任务应该只取决于相关类的行为,而不是它们的具体类型
绕过继承限制的一种方法是使用访问者模式,它本质上通过类对访问请求的反应来公开继承层次结构中类的具体类型
有一种情况是,当用户的代码库无法控制时,可能会添加额外的派生类,而这种情况下,您无法使用访问者模式绕过基于继承的设计。但据我所知,这不是一个问题
因此,另一种方法是通过variant
而不是继承(我在这里使用的是c++17std::variant
s,但您也可以使用Boost.variant,如果您无法访问已经实现std::variant
的编译器/STL),只需将您的类型定义为
class Type1 {
public:
void print() const {
std::cout << "I am of type 1\n";
}
};
class Type2 {
public:
void print() const {
std::cout << "I am of type 2\n";
}
};
该操作本身如下所示:
class Action {
Type t_;
public:
Action(Type t)
: t_(std::move(t)) {
}
void print() {
auto action = make_combined(
[](Type1 const& t) { std::cout << "Called with argument of type Type1\n"; t.print(); },
[](Type2 const& t) { std::cout << "Called with argument of type Type2\n"; t.print(); }
);
std::visit(action, t_);
}
};
int main() {
Type t1 = Type1{};
auto a1 = Action{t1};
a1.print();
auto a2 = Action{Type2{}};
a2.print();
}
工作示例可在上找到
如果您以后想添加一个附加类型Type3
,只需创建它,然后使用声明将它添加到type
中的变量中,编译器就会告诉您需要在哪里为新类型添加新的处理程序
如果您喜欢操作
类中未明确提及的类型的某种默认行为,您也可以只向操作
类中的组合lambda添加一个通用lambda,如:
auto action = make_combined(
[](Type1 const& t) { std::cout << "Called with argument of type Type1\n"; t.print(); },
[](Type2 const& t) { std::cout << "Called with argument of type Type2\n"; t.print(); },
[](auto const& t) { std::cout << t.print(); } // this will be selected for all other types
);
auto action=make\u组合(
[](Type1 const&t){std::cout使用混合,您可以非常自然地编写类。您只需扩展CRTP模式,使它们能够在以后使用混合技术。代码比任何文本解释都简单,因此请查看您自己:
#include <iostream>
#include <memory>
class Type
{
public:
virtual void Do()=0;
};
template <typename Base>
class Type1: public Base
{
void Do() override { std::cout << "Type1::Do" << std::endl; }
};
template <typename Base>
class Type2: public Base
{
void Do() override { std::cout << "Type2::Do" << std::endl; }
};
template <typename Base>
class Action: public Base
{
public:
virtual void Print()=0;
};
template <typename Base>
class Action1: public Base
{
void Print() override { std::cout << "Print for Action1" << std::endl;}
};
template <typename Base>
class Action2: public Base
{
void Print() override { std::cout << "Print for Action2" << std::endl;}
};
int main()
{
// what we want "mixin" is Action into Type
using Base = Action<Type>;
std::unique_ptr<Base> a1 { new Action1<Type1<Base>> }; // Action1 in Type1
std::unique_ptr<Base> a2 { new Action2<Type2<Base>> }; // Action2 in Type2
a1->Do();
a1->Print();
a2->Do();
a2->Print();
}
#包括
#包括
类类型
{
公众:
虚空Do()=0;
};
模板
类别类型1:公共基础
{
void Do()override{std::cout下面是我的结论
#include <iostream>
using namespace std;
/** Class Type and its derived classes Type1 and Type2
*/
class Type {
public:
int type;
virtual void print() = 0;
};
class Type1 : public Type {
public:
Type1() { type = 1; }
void print() { cout << "I am of type 1" << endl; }
};
class Type2 : public Type {
public:
Type2() { type = 2; }
void print() { cout << "I am of type 2" << endl; }
};
/** Class Action and its derived classes Action1 and Action2
*/
class Action {
protected:
Type *t;
public:
Action(Type *t) : t(t) {};
static Action *make_action(Type *t);
virtual void print() = 0;
};
class Action1 : public Action {
public:
Action1(Type *t) : Action(t) {};
void print() {
cout << "I am an action1. My type says: ";
t->print();
}
};
class Action2 : public Action {
public:
Action2(Type *t) : Action(t) {};
void print() {
cout << "I am an action2. My type says: ";
t->print();
}
};
Action *Action::make_action(Type *t) {
switch(t->type) {
case 1:
return new Action1(t);
break;
case 2:
return new Action2(t);
break;
}
}
/** Main
*/
int main(int argc, const char * argv[]) {
Type *ta = new Type1();
Action *acta = Action::make_action(ta);
acta->print();
delete acta, ta;
Type *tb = new Type2();
Action *actb = Action::make_action(tb);
actb->print();
delete actb, tb;
return 0;
}
似乎是预期的工作。如果有人看到错误,请让我知道。
第一句话:我个人认为“使用命名空间”是一种不好的做法,因为它通过头文件波动。原因是在因特网上描述的。< /P>
也是,由于异常安全,我总是尽量减少指针的使用。萨特有一本关于它的好书,叫做Excel C++,它详细描述了这些问题。但是指针显然有其用途,特别是多态性。最后,我喜欢制作类结构,如果它们只有公共成员…这只是品味的问题
让我们从一些代码开始:
#include <string>
#include <iostream>
#include <memory>
struct Type
{
virtual void print() = 0;
};
struct Type1 : Type
{
void print() { std::cout << "I am of type 1" << std::endl; }
};
struct Type2 : Type
{
void print() { std::cout << "I am of type 2" << std::endl; }
};
class Action
{
protected:
std::unique_ptr<Type> t;
public:
Action(std::unique_ptr<Type> &&t) : t(std::move(t)) {};
void print()
{
std::cout << "I am an action. My type says: ";
t->print();
}
};
int main()
{
Action act(std::make_unique<Type1>());
act.print();
return 0;
}
#包括
#包括
#包括
结构类型
{
虚空打印()=0;
};
结构类型1:类型
{
void print(){std::cout child=input->child->Accept(此)*/
返回输入;
}
虚拟类型*句柄(类型2*输入){返回输入;}
};
结构转储操作:TypeVisitor
{
虚拟类型*句柄(类型1*输入)重写
{
std::难道我看不出问题所在吗…在我看来,它正是你想要的,对吗?你把功能放在Type1/Type2中,并使用多态性来区分实现;听起来不错…@atlaste:事实上,我希望动作根据t的类型有不同的行为。该行为当然是th的行为e组件以及所有子组件。如果需要不同的操作行为,则需要不同的实现。但可能您正在寻找访问者模式?如果操作取决于对象的具体类型而不是其多态行为,则可能不希望在此处使用继承。您可以解决这些问题如前所述,使用基于继承的多态性和访问者。但我认为更干净的方法是根本不使用公共基类,只使用Boost或c++17变体。只有在您始终使用所有可能的“实例”时才有效。如果这是库的一部分,用户可以添加自己的de
int main() {
Type t1 = Type1{};
auto a1 = Action{t1};
a1.print();
auto a2 = Action{Type2{}};
a2.print();
}
auto action = make_combined(
[](Type1 const& t) { std::cout << "Called with argument of type Type1\n"; t.print(); },
[](Type2 const& t) { std::cout << "Called with argument of type Type2\n"; t.print(); },
[](auto const& t) { std::cout << t.print(); } // this will be selected for all other types
);
#include <iostream>
#include <memory>
class Type
{
public:
virtual void Do()=0;
};
template <typename Base>
class Type1: public Base
{
void Do() override { std::cout << "Type1::Do" << std::endl; }
};
template <typename Base>
class Type2: public Base
{
void Do() override { std::cout << "Type2::Do" << std::endl; }
};
template <typename Base>
class Action: public Base
{
public:
virtual void Print()=0;
};
template <typename Base>
class Action1: public Base
{
void Print() override { std::cout << "Print for Action1" << std::endl;}
};
template <typename Base>
class Action2: public Base
{
void Print() override { std::cout << "Print for Action2" << std::endl;}
};
int main()
{
// what we want "mixin" is Action into Type
using Base = Action<Type>;
std::unique_ptr<Base> a1 { new Action1<Type1<Base>> }; // Action1 in Type1
std::unique_ptr<Base> a2 { new Action2<Type2<Base>> }; // Action2 in Type2
a1->Do();
a1->Print();
a2->Do();
a2->Print();
}
#include <iostream>
using namespace std;
/** Class Type and its derived classes Type1 and Type2
*/
class Type {
public:
int type;
virtual void print() = 0;
};
class Type1 : public Type {
public:
Type1() { type = 1; }
void print() { cout << "I am of type 1" << endl; }
};
class Type2 : public Type {
public:
Type2() { type = 2; }
void print() { cout << "I am of type 2" << endl; }
};
/** Class Action and its derived classes Action1 and Action2
*/
class Action {
protected:
Type *t;
public:
Action(Type *t) : t(t) {};
static Action *make_action(Type *t);
virtual void print() = 0;
};
class Action1 : public Action {
public:
Action1(Type *t) : Action(t) {};
void print() {
cout << "I am an action1. My type says: ";
t->print();
}
};
class Action2 : public Action {
public:
Action2(Type *t) : Action(t) {};
void print() {
cout << "I am an action2. My type says: ";
t->print();
}
};
Action *Action::make_action(Type *t) {
switch(t->type) {
case 1:
return new Action1(t);
break;
case 2:
return new Action2(t);
break;
}
}
/** Main
*/
int main(int argc, const char * argv[]) {
Type *ta = new Type1();
Action *acta = Action::make_action(ta);
acta->print();
delete acta, ta;
Type *tb = new Type2();
Action *actb = Action::make_action(tb);
actb->print();
delete actb, tb;
return 0;
}
I am an action1. My type says: I am of type 1
I am an action2. My type says: I am of type 2
#include <string>
#include <iostream>
#include <memory>
struct Type
{
virtual void print() = 0;
};
struct Type1 : Type
{
void print() { std::cout << "I am of type 1" << std::endl; }
};
struct Type2 : Type
{
void print() { std::cout << "I am of type 2" << std::endl; }
};
class Action
{
protected:
std::unique_ptr<Type> t;
public:
Action(std::unique_ptr<Type> &&t) : t(std::move(t)) {};
void print()
{
std::cout << "I am an action. My type says: ";
t->print();
}
};
int main()
{
Action act(std::make_unique<Type1>());
act.print();
return 0;
}
int main()
{
std::string s;
std::getline(std::cin, s);
std::unique_ptr<Type> type;
if (s == "1")
{
type = std::make_unique<Type1>();
}
else
{
type = std::make_unique<Type2>();
}
Action act(std::move(type));
act.print();
return 0;
}
struct TypeVisitor;
struct Type
{
virtual Type* Accept(TypeVisitor& visitor) = 0;
};
template <typename T>
struct TypeBase : Type
{
virtual Type* Accept(TypeVisitor& visitor) override
{
return visitor.Handle(static_cast<T*>(this));
}
};
struct Type1 : TypeBase<Type1>
{
};
struct Type2 : TypeBase<Type2>
{
};
struct TypeVisitor
{
virtual Type* Handle(Type1* input)
{
/* if necessary, recurse here like: input->child = input->child->Accept(this); */
return input;
}
virtual Type* Handle(Type2* input) { return input; }
};
struct DumpAction : TypeVisitor
{
virtual Type* Handle(Type1* input) override
{
std::cout << "Handling type 1." << std::endl;
return TypeVisitor::Handle(input);
}
virtual Type* Handle(Type2* input) override
{
std::cout << "Handling type 2." << std::endl;
return TypeVisitor::Handle(input);
}
};
int main()
{
DumpAction act;
Type2 type2;
type2.Accept(act);
return 0;
}