c++ 指针和模板类

w6mmgewl  于 2022-12-24  发布在  其他
关注(0)|答案(2)|浏览(121)
    • 说明:**

我试图根据用户输入创建一个类对象。我试图用指针存储它的引用,并在稍后阶段使用它。与Dart语言中的变量类型late类似的想法。是否可以在CPP中进行此操作?如果可以,我应该使用哪种类型的变量?

    • 代码:**
#include <iostream>
#include <vector>
using namespace std;

template <typename T>
class foo {
    public:
        T item;
        foo(){}
};

class bar_1 {
    public:
        int value = 99;
        void randomise();
};

class bar_2{
    public:
        string value = "init";
        void randomise();
};

int main()
{
    int i;
    cin >> i;
    void* ptr;
    if (i > 0) {
        ptr = new foo<bar_1>;
    }
    else {
        ptr = new foo<bar_2>;
    }

    // maybe run the function to randomise 'value'
    // ptr->item.randomise();
    cout << ptr->item.value;
    return 0;
}
    • 错误:**
cout << ptr->item.value;
|          ^~
error: ‘void*’ is not a pointer-to-object type
    • 编辑1:**我不允许修改类栏atm,aschepler的#1解决方案最适合。
    • 编辑2:**我正在重新考虑这个问题。我发现最直接的方法是使用一个模板转储并将它们转换成一个类层次结构,其中'bar 1'和'bar 2'是'foo'的后代类。因此,如果指向类'foo'的指针存在于作用域中,它可以被赋值为指向'bar 1'或'bar 2'对象的指针。
    • 更新代码:(类层次结构)**
#include <iostream>

using namespace std;
class foo {
public:
    // common function going to be overrided by children classes
    virtual void randomize() {
    }
};

class bar_1: public foo {
public:
    int value = 99;
    // bar_1 version of randomize()
    void randomize() override {
        cout << "Turning int from " << value << " to a new int\n";
        value++; // increment by 1
    }
};

class bar_2: public foo {
public:
    std::string value = "something";
    // bar_2 version of randomize()
    void randomize() override {
        cout << "Turning string from " << value << " to a new string\n";
        value = "new thing"; // diff string
    }
};

int main()
{
    int type = 1; // e.g type can variate from 1 to 10
    foo* ptr;
    switch(type)
    {
        case 1:
            ptr = new bar_1();
            break;
        case 2:
            ptr = new bar_2();
            break;
        // case 3:
        //     ...
        default:
            cout << "Invalid type\n";
            break;
    }
    ptr->randomize();
    return 0;
}
    • 更新代码:(继续使用模板类)**
#include <iostream>
#include <variant>
#include <optional>
using namespace std;

template <typename T>
class foo {
    public:
        T item; // an object either bar_1 or bar_2
        foo(T _item) : item(_item) {} // constructor assigning in-class variable "item"
        T getItem() // template function returning the in-class variable item
        {
            return item;
        }
};

class bar_1 {
    public:
        int value = 99;
};

class bar_2{
    public:
        string value = "str";
};

int main()
{
    int i = 0; // some value
    optional<variant<foo<bar_1>, foo<bar_2>>> foo_foo;
    if (i > 0) {
        foo_foo = foo(bar_1());
    }
    if (i < 0) {
        foo_foo = foo(bar_2());
    }
    // using visit
    // if i > 0, it gives 99 as foo.item has type bar_1, which has an int value 99
    // if i < 0, it gives "str" as foo.item has type bar_2, which has an string value "str"
    // if i == 0, it gives 0 as the optioanl variable foo_foo contains no values
    visit([](auto &_foo) { cout << _foo.getItem().value; }, *foo_foo);
    return 0;
}
wqnecbli

wqnecbli1#

当然不是像JS和(AFAIK)Dart这样的动态语言所允许的方式。
C++是静态类型的,所以当你在一个(非模板的)函数中写cout << ptr->item.value时,必须知道itemvalue的类型,不能在运行时混淆不相关的类型,比如intstring
然而,你也可以使用继承来达到你想要的效果。一个对象的指针总是可以被转换成指向它的(可访问的,例如public)基的指针,并且像基一样被访问--但是对象将保留它的实际类型,并且base的所有 virtual 方法将作用于该类型,并且可以被覆盖:

class foo {
public:
    virtual ~foo() = default; // optional but highly recommended
    virtual randomize() {
        // the default implementation
    }
    // or: virtual randomize() = 0; // if you want ALL subclasses to override it
};

class bar_1: public foo {
public:
    int value = 99;
    void randomize() override {
        // the bar_1-specific implementation
    }
};

class bar_2: public foo {
public:
    std::string value = "something";
    void randomize() override {
        // the bar_2-specific implementation
    }
};

...

foo *obj = new bar_1(); // create an object of type bar_1, but treat it as foo
obj->randomize(); // will call bar_1::randomize as obj points to an object of type bar_1
// obj->value = 42; // won’t work: value is not a member of foo
delete obj;

obj = new bar_2(); // now, create an object of type bar_2, but treat it as foo again
obj->randomize(); // will call bar_2::randomize as obj now points to an object of type bar_2
delete obj;

或使用智能指针(强烈推荐):

std::unique_ptr<foo> obj = std::make_unique<bar_1>()
// std::unique_ptr<foo> obj{new bar_1()}; // if you can’t afford C++17
obj->randomize();

obj = std::make_unique<bar_2>();
// obj.reset(new bar_2()); // if you can’t afford C++17
obj->randomize();
5sxhfpxr

5sxhfpxr2#

正如注解所指出的,不能在块结束后使用指向块中声明的变量的指针,可以使用std::unique_ptrstd::optional来解决这个问题。
这里有两种可能的解决方案。

1:std::variant<foo<bar_1>, foo<bar_2>>可以包含任何一种类型的对象,而不需要对现有的类做任何修改(也不需要任何动态分配),然后我们可以使用std::visit来处理它所包含的任何对象。

#include <variant>
#include <optional>
int main()
{
    int i;
    std::cin >> i;
    std::optional<std::variant<foo<bar_1>, foo<bar_2>>> the_foo;
    if (i > 0) {
        the_foo = foo<bar_1>{};
    }
    else {
        the_foo = foo<bar_2>{};
    }

    // run the function to randomise 'value'
    std::visit([](auto &foo) { foo.item.randomise(); }, *the_foo);
    std::visit([](auto &foo) { std::cout << foo.item.value; }, *the_foo);
    return 0;
}

2如果你可以改变类,注意bar_1bar_2都包含一些公共操作“randomise item”和“print item”。所以我们可以创建一个接口,允许多态使用这些操作,而不需要知道实际类型。如果你以后添加其他类似的类,这也更容易扩展。

class IBar {
public:
    virtual ~IBar() = default;
    virtual void print(std::ostream& os) const = 0;
    virtual void randomise() = 0;
};

class bar_1 : public IBar {
public:
    int value;

    void print(std::ostream& os) const override
    { os << value; }

    void randomise() override;
};

class bar_2 : public IBar {
public:
    std::string value;

    void print(std::ostream& os) const override
    { os << value; }

    void randomise() override;
};

现在foo甚至不需要是一个模板,它只需要使用一个接口指针来代替实际类型的成员:

#include <memory>
class foo {
public:
    std::unique_ptr<IBar> pItem;
    explicit foo(std::unique_ptr<IBar> p) : pItem(std::move(p)) {}
    foo() = default;
};

int main()
{
    int i;
    std::cin >> i;
    foo the_foo;
    if (i > 0) {
        the_foo.pItem = std::make_unique<foo<bar_1>>();
    }
    else {
        the_foo.pItem = std::make_unique<foo<bar_2>>();
    }

    // run the function to randomise 'value'
    the_foo.pItem->randomise();
    the_foo.pItem->print(std::cout);
    return 0;
}

相关问题