如何从没有共享超类的类调用公共方法
C++ How to call common method from classes without a shared superclass?
下面的代码给出了一个交叉/跳转标签初始化编译错误。但是我怎样才能达到我想要达到的效果呢?也就是说,只实例化我真正需要的类,然后通用地调用所有类的通用方法?
类A和类B实际上不在我的代码中,而是在我正在使用的一个大型库中,因此不能更改以提供帮助。它们不是超类的子类(这可以解决问题)。
两个真实的类处理相似的数据,因此以下面所示的方式与filter()方法兼容。我知道一些丑陋的C代码可以使它工作,但我正在寻找c++惯用的解决方案。
在实际问题中,有更多的代码和更多的情况,构造函数和类方法是资源密集型的,所以我不能只是初始化所有可能的类"以防万一",然后用switch()选择正确的filter()方法。
#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;
}
}
}
这是可行的,尽管有点令人讨厌:
#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();
}
使用模板可以解决这个问题:
template <class F>
void doFilterStuff()
{
F f("Your string");
f.filter();
}
int main()
{
doFilterStuff<A>();
doFilterStuff<B>();
}
从中受益:更少的代码,更多的抽象,没有样板代码。编译器检查模板化方法的所有实例化是否兼容:例如,不提供filter
-方法的C类实例化将导致编译时错误。
模板是用来解决这个问题的:为未连接的类型提供相同的功能,这些类型至少部分地暴露相同的接口。
@NikBougalis在评论中正确地指出,如果你需要为每个类型调用特殊的方法,事情会变得有点丑陋的模板,但这是完全可行的。示例代码有点长,所以我创建了这个演示。
所以想法是这样的:我们定义一个新的类AB
,它有一个类似于我们期望从A
和B
得到的接口的"接口"。这个新类内部包含指向A
和B
的指针,它们是动态分配的——这允许我们在运行时通过创建这个新类的实例来定义对象的"身份",并根据需要指示AB
构造A
或B
。
对A
和B
的所有访问都是通过AB
公开的接口。
class AB {
A *a;
B *b;
public:
AB()
: a(nullptr), b(nullptr)
{ }
~AB()
{
delete a;
delete b;
}
void CreateA(std::string s)
{
if((a != NULL) || (b != NULL))
return;
a = new A(s);
}
void CreateB(std::string s)
{
if((a != NULL) || (b != NULL))
return;
b = new B(s);
}
void filter()
{
if(a)
{
a->filter();
return;
}
if(b)
{
b->filter();
return;
}
}
};
int main() {
int type = 1;
AB ab;
switch (type) {
case 1:
ab.CreateA("hi from A");
break;
case 2:
ab.CreateB("hello from B");
break;
}
ab.filter();
}
假设您已经在某处有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();
}
实际上,这并不是您所期望的,您使用两个不同的(不相关的)类来生成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;
}
在你的例子中,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();
}
如果你不能得到一个通用的基类,你必须使用包装器
- 在c++中多次调用方法
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 接收字符串并使用它来调用方法C++
- 使用 gmock c++ 在真实对象上调用方法
- 不带预处理器的调用方法/文件的文件名/行号
- JNI从Android调用C++方法
- 在 C++ 的 Switch Case 中创建对象后对对象调用方法
- 如何使用接口指针调用方法,该指针是其具体类的一部分,而不是接口的一部分
- 从内部类中的方法从包含类调用方法
- 在销毁期间从另一个线程调用对象上调用方法是否未定义行为?
- 为什么派生类的实例从基类调用方法?
- 实例化对象并调用方法,使用单行语法在 C# 或 C++ 中返回值?
- C++当您取消引用指向类对象的指针,然后将其作为引用返回时,是否可以对此引用调用方法
- 通过模板函数对未知类型调用方法
- 从基于迭代器的for循环转换后,如何在map::find()中调用方法
- 创建一个C++DLL以从C#DLL调用方法
- 如何从qt中的类中调用方法
- 如何在不迭代的情况下对数组中的每个元素调用方法
- C++11 - 获取编译时的所有类变量,并在没有 Boost 的情况下为它们调用方法
- C ++:如何在不创建对象的情况下在主函数中调用方法