以下代码给出了交叉/跳转标签初始化编译错误,当然。但是我如何获得我想要达到的效果呢?也就是说,只实例化我真正需要的类,然后一般调用所有类共有的方法?
A类和B类实际上不在我的代码中,但在我正在使用的大型库中,因此无法更改为帮助。他们不是超类的孩子(这将解决问题)。
两个真实类都处理类似的数据,因此以下面说明的方式与filter()方法兼容。我知道一些可能用于使其工作的丑陋C黑客,但我正在寻找C ++惯用解决方案。
在真正的问题中,有更多的代码和更多的情况,构造函数和类方法是资源密集型的,所以我不能只是“以防万一”初始化所有可能的类,然后选择正确的过滤器()方法用switch()。
#include <string>
#include <iostream>
class A {
public:
std::string msg;
A(std::string s) { msg = s;}
void filter() { std::cout << "Message A = " << msg << std::endl;}
};
class B {
public:
std::string msg;
B(std::string s) { msg = s;}
void filter() { std::cout << "The B message: " << msg << std::endl;}
};
int main() {
int type = 1;
switch (type) {
case 1:
A f("hi from A");
break;
case 2:
B f("hello from B");
break;
}
f.filter();
}
编辑:根据@ stefan的回答,我修改了我的代码,看起来像下面的内容。我还没有在真实的情况下尝试过,但我相信它会奏效。 (谢谢大家!)
#include <string>
#include <iostream>
class A {
public:
std::string msg;
A(std::string s) { msg = s;}
void filter() { std::cout << "Message A = " << msg << std::endl;}
};
class B {
public:
std::string msg;
B(std::string s) { msg = s;}
void filter() { std::cout << "The B message: " << msg << std::endl;}
};
template <class F>
void doFilterStuff(std::string msg) {
F f(msg);
f.filter();
}
int main() {
for (int i=1; i<4; i++) {
std::cout << "Type = " << i << std::endl;
switch (i) {
case 1:
doFilterStuff<A>("hi from A");
break;
case 2:
doFilterStuff<B>("hello from B");
break;
default:
std::cout << "Throwing an error exception" << std::endl;
}
}
}
答案 0 :(得分:5)
虽然这有点令人讨厌:
#include <string>
#include <iostream>
class A {
public:
std::string msg;
A(std::string s) { msg = s;}
void filter() { std::cout << "Message A = " << msg << std::endl;}
};
class B {
public:
std::string msg;
B(std::string s) { msg = s;}
void filter() { std::cout << "The B message: " << msg << std::endl;}
};
// -------------
class Base
{
public:
virtual void filter() = 0;
virtual ~Base() {}
};
template<class C>
class Wrapper: public Base
{
public:
Wrapper( C * impl ): m_impl(impl) { }
~Wrapper() { delete m_impl; }
virtual void filter()
{
m_impl->filter();
}
private:
C * m_impl;
};
// -------------
int main() {
Base * f = NULL;
int type = 1;
switch (type) {
case 1:
f = new Wrapper<A>(new A("hi from A"));
break;
case 2:
f = new Wrapper<B>(new B("hello from B"));
break;
}
f->filter();
delete f;
}
C ++ 11,异常安全的变体,完美转发构造函数。仅Wrapper
和main()
与上述不同,此处为:
template<typename T>
class Wrapper : public Base
{
public:
template<typename... Args>
Wrapper(Args&&... args) : m_impl(std::forward<Args>(args)...) {}
virtual void filter() {
m_impl.filter();
}
private:
T m_impl;
};
// -------------
int main()
{
std::unique_ptr<Base> f;
int type = 1;
switch (type) {
case 1:
f.reset(new Wrapper<A>("hi from A"));
break;
case 2:
f.reset(new Wrapper<B>("hello from B"));
break;
}
f->filter();
}
答案 1 :(得分:4)
使用模板可以解决这个问题:
template <class F>
void doFilterStuff()
{
F f("Your string");
f.filter();
}
int main()
{
doFilterStuff<A>();
doFilterStuff<B>();
}
受益于此:更少的代码,更多的抽象,没有样板代码。编译器检查模板化方法的所有实例化是否兼容:例如。 C类实例化不提供filter
方法会导致编译时错误。
为此问题的所有内容制作模板:为未连接的类型提供相同的功能,这些类型至少部分地暴露了相同的接口。
@NikBougalis在评论中正确地指出,如果你需要为每种类型调用特殊方法,那么模板会让事情变得有点丑陋,但这是完全可行的。示例代码有点太长,所以我创建了这个demo。
答案 2 :(得分:2)
所以我们的想法是这样的:我们定义了一个新的类AB
,它有一个类似于我们从A
和B
中预期的界面的“界面”。这个新类内部包含动态分配的A
和B
指针 - 这允许我们通过创建这个新类的实例并指示{{}来定义运行时对象的“标识”。 1}}根据需要构建AB
或A
。
B
和A
的所有访问都是通过B
公开的界面。
AB
答案 3 :(得分:1)
Aassuming你有A或B的对象已经存在并且他们的破坏不是这样的,在C ++ 11中你可以使用std :: function和std :: bind
#include <string>
#include <iostream>
#include <functional>
struct A {
std::string msg;
A(std::string const & s) : msg(s) {}
void filter() { std::cout << "Message A = " << msg << std::endl;}
};
struct B {
std::string msg;
B(std::string s) : msg(s) {}
void filter() { std::cout << "The B message: " << msg << std::endl;}
};
int main() {
int type = 1;
//assuming You have objects already somewehere
A a("hi from A");
B b("hello from B");
std::function<void()> filter_func;
switch (type) {
case 1:
filter_func = std::bind( &A::filter, &a );
break;
case 2:
filter_func = std::bind( &B::filter, &b );
break;
default:
throw "Missing case";
}
filter_func();
}
答案 4 :(得分:-1)
实际上,这不符合您的期望,您使用两个不同的(不相关的)类来生成f
,然后调用f.filter()
- 它将始终使用B
变体,有时它不会被初始化。
您可以通过将f.filter移动到每个案例中并使用大括号包装来修复它:
switch (type) {
case 1:
{
A f("hi from A");
f.filter();
}
break;
case 2:
{
B f("hello from B");
f.filter();
}
break;
}
这将满足您的期望。
另一种选择是创建一个具有虚拟filter
函数的基类,并从中派生出来。然后使用指向该类的指针,并调用new
来创建A或B类。像这样:
class Base
{
public:
std::string msg;
virtual void filter() = 0;
};
class A: public Base {
public:
A(std::string s) { msg = s;}
void filter() { std::cout << "Message A = " << msg << std::endl;}
};
class B : public Base {
public:
B(std::string s) { msg = s;}
void filter() { std::cout << "The B message: " << msg << std::endl;}
};
int main() {
int type = 1;
Base *f = NULL;
switch (type) {
case 1:
f = new A("hi from A");
break;
case 2:
f = new B("hello from B");
break;
}
if (f)
f->filter();
else
cout << "Huh? Didn't set 'f', bad type?" << endl;
delete f;
}
答案 5 :(得分:-3)
在你的例子中,f是一个局部变量。
您可以尝试这样的事情
class Base{
public :
virtual ~Base(){}
virtual void filter(){}
};
class A : public Base{
public :
A(const char * str){}
void filter(){}
};
class B : public Base{
public :
B(const char * str){}
void filter(){}
};
int main(){
int type = 1;
Base *f = 0;
switch (type) {
case 1:
f = new A("hi from A");
break;
case 2:
f = new B("hello from B");
break;
}
if (f)
f->filter();
}
如果无法获得公共基类,则必须使用包装器