c++ 成员变量和对象切片

bf1o4zei  于 2023-01-10  发布在  其他
关注(0)|答案(2)|浏览(144)

This answer建议使用指针来克服向量中的对象切片。从我的测试来看,当使用基类和派生类之间共享的变量时,这是正确的。例如,给定以下类:

class Base {
public:
     int x = 1;
};

class Derived : public Base {
public:
    Derived() : Base() { 
        x = 2;
    }
    int y;
};

可以看出,Derived在构造时将x重新定义为2。2当被放入基类的向量中时,x变量的行为与预期的一样。

vector<unique_ptr<Base>> baseVec;               // Vector of pointers to Base objects
unique_ptr<Derived> derivedPtr(new Derived());  // Pointer to Derived object
baseVec.push_back(std::move(derivedPtr));       // Add pointer with std::move()
std::cout << baseVec[0]->x << std::endl;        // Outputs 2

但是,尝试使用仅属于Derived的y变量会导致错误C2039: 'y' is not a member of 'Base',如下所示:

std::cout << baseVec[0]->y << std::endl;

有没有一种方法可以绕过这个问题,同时保留派生类的唯一成员变量,如果没有,有没有更好的方法将对象存储在有序容器中?

    • 编辑**

之前编辑的相关内容已经移到下面我的答案中。在我找到使用智能指针的方法之前(或者确保我的原始指针实现没有内存问题),我不打算接受这个答案。

nxowjjhe

nxowjjhe1#

有没有一种方法可以绕过这个问题,同时保留派生类的唯一成员变量,如果没有,有没有上级的方法将对象存储在有序容器中?
有很多技术可以让你达到类似的效果,所有这些技术都已经在SO的不同答案中提到了。
简历:简短的(可能不完整的)简历:

  • 双重分派完全基于多态性,基本思想是将访问者类传递给存储在容器中的示例,这些示例提升自己,通过重载方法将派生类型传递回访问者。
  • 类型擦除。你可以用几种方式来实现。例如,你可以将你的派生类存储为指向void或基类的指针沿着指向一个接收擦除类型的函数的指针。指向函数的指针应该是一个函数模板的专用化地址,该模板能够将类静态强制转换回其原始类型,并使用它执行任何任务。
  • 标记联合。你不直接存储你的派生类,而是把它们 Package 在一个专用的结构体中,这个结构体携带足够的信息,通过一组标记(即匿名枚举)把它们强制转换回派生类型。
  • 从C++17开始你可以使用std::variant,这是一种类型安全的标记联合,你可以通过一个专门的访问者来访问。
  • AOB(任何其他业务)。

我不认为重复SO上已有的例子是值得的。你可以使用上面的关键字搜索它们,得到你想要的。
每种技术都有其优点和缺点。其中一些技术允许您在调用者的上下文中提取原始类型,这样您就可以在需要时将对象传递给其他函数。其他一些技术则完全删除类型,并提供 * handler * 来对原始类型执行操作。但是在调用者的上下文中却永远无法将其恢复。这些差异通常会影响性能(在某种程度上你很难观察到)因为所使用的语言的特点和它们所吸引的演员。

0s7z1bwu

0s7z1bwu2#

改变课程

BaseDerived的类定义应更改为:

class Base {
public:
    int x = 1;

    virtual void f() { }
};

class Derived : public Base {
public:
    Derived() : Base() { x = 2; }
    int y = 55;

    void f() { }
};

值得注意的是增加了虚函数void f(),它允许dynamic_cast处理指针的强制转换。

使用唯一指针

唯一指针可用于存储、检索、修改和安全删除指向Derived对象的指针,这些指针驻留在指向Base对象的指针向量中。下面的示例使用堆分配对象。

/* Insertion */
// Create a vector of unique_ptr to Base and add a unique_ptr to Derived to it
std::vector<std::unique_ptr<Base>> v;
std::unique_ptr<Derived> p1(new Derived());
v.push_back(std::move(p1));

/* Retrieval */
// Release the pointer at base to place it into p2
std::unique_ptr<Derived> p2(dynamic_cast<Derived*>(v[0].release()));

/* Modification */
p2->x = 0xff;    // Modify x (Decimal 255)
p2->y = 0xffff;  // Modify y (Decimal 65535)
int y = p2->y;   // Copy the value of the Derived object's y to an int for use later

// Move the pointer to the modified object back to v
v[0] = std::move(p2); 

/* Output */
std::cout << v[0]->x << std::endl; // Outputs 255
std::cout << y << std::endl;       // OUtputs 65535

/* Delete */
// To safely delete the object, it must be treated as a pointer to a Derived object
// Thus it must be released from the control of v and deleted via p3, which is cast in the same manner as p2 was
std::unique_ptr<Derived> p3(dynamic_cast<Derived*>(v[0].release()));
p3.reset(); // This deletes the object as well, not just the reference to it!

按预期运行并输出25565535

相关问题