1

下面的代码当然给出了交叉/跳转标签初始化编译错误。但是我怎样才能得到我想要达到的效果呢?也就是只实例化我真正需要的类,然后泛型调用所有类通用的方法?

A 类和 B 类实际上不在我的代码中,而是在我正在使用的大型库中,因此无法更改以提供帮助。他们不是超类的孩子(这将解决问题)。

两个真实类都处理相似的数据,因此以下面说明的方式与 filter() 方法兼容。我知道一些丑陋的 C hack 可以用来使它工作,但我正在寻找 C++ 惯用的解决方案。

在真正的问题中,有更多的代码和更多的案例,并且构造函数和类方法是资源密集型的,所以我不能“以防万一”初始化所有可能的类,然后选择正确的 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;
        }
    }
}
4

6 回答 6

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,异常安全的变体,具有完美的构造函数转发。只是Wrappermain()上面的不同,这里它们是:

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();
}
于 2013-05-07T22:51:03.243 回答
4

使用模板可以解决这个问题:

template <class F>
void doFilterStuff()
{
   F f("Your string");
   f.filter();
}

int main()
{
   doFilterStuff<A>();
   doFilterStuff<B>();
}

从中受益:更少的代码,更多的抽象,没有样板代码。编译器检查模板化方法的所有实例化是否兼容:例如,使用不提供 - 方法的类 C 的实例化filter将导致编译时错误。

模板是针对这个问题的全部内容而制作的:为未连接的类型提供相同的功能,这些类型至少部分地公开相同的接口。

@NikBougalis 在评论中正确指出,如果您需要为每种类型调用特殊方法,模板会变得有点难看,但这是完全可行的。示例代码有点太长,所以我创建了这个演示

于 2013-05-07T22:53:55.333 回答
2

所以想法是这样的:我们定义一个新的类AB,它有一个“接口”,类似于我们期望的接口AB。这个新类在内部包含指向AB动态分配的指针——这允许我们在运行时通过创建这个新类的实例来定义对象的“身份”,并根据需要指示AB构造 anA或 a B

所有访问AB通过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();           
}
于 2013-05-07T22:50:09.730 回答
1

A假设您已经在某个地方拥有 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();
}
于 2013-05-08T23:18:48.463 回答
-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;
}
于 2013-05-07T22:50:10.330 回答
-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();

}

如果你不能得到一个通用的基类,你必须使用一个包装器

于 2013-05-07T22:59:39.567 回答