无法在派生对象上运行虚拟函数



我有一个程序可以从文件中读取内容并创建对象。
我将这些对象存储到vector<Component *>中。所以我可以在每个对象上调用一个虚函数。
虚拟功能不起作用。任何其他类函数在矢量对象上都可以正常工作。
有人知道发生了什么吗?

void upload(vector<Component*>);
vector<Component*>download();
int main() {
vector<Component*>vec = download();
vec[0]->printSpecs(); // read acces violation
// Write all products from vector to file (overwrite)
}
void upload(vector<Component*> x) {
// write each class object to specific file
ofstream f;
for (int i = 0; i < x.size(); i++) {
if (x[i]->rank() == 1) {
f.open("CPU.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(CPU));
f.close();
}
if (x[i]->rank() == 2) {
f.open("Case.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(Case));
f.close();
}
if (x[i]->rank() == 3) {
f.open("PS.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(PS));
f.close();
}
if (x[i]->rank() == 4) {
f.open("GPU.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(GPU));
f.close();
}
if (x[i]->rank() == 5) {
f.open("HD.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(HD));
f.close();
}
if (x[i]->rank() == 6) {
f.open("RAM.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(RAM));
f.close();
}
if (x[i]->rank() == 7) {
f.open("SSD.txt", ios::binary);
f.write(reinterpret_cast<char*>(x[i]), sizeof(SSD));
f.close();
}
}
}
vector<Component*> download() {
vector<Component*>items;
ifstream f;
// read cpu objects into vector
f.open("CPU.txt", ios::binary);
while (!f.eof()) {
static CPU temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(CPU));
Component* x = &temp;
items.push_back(x);
}
items.pop_back();
f.close();
// read GPU objects into vector
f.open("GPU.txt", ios::binary);
while (!f.eof()) {
static GPU temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(GPU));
Component* x = &temp;
items.push_back(x);
}
items.pop_back();
f.close();
// read HD objects into vector
f.open("HD.txt", ios::binary);
while (!f.eof()) {
static HD temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(HD));
Component* x = &temp;
items.push_back(x);
}
items.pop_back();
f.close();
// read PS objects into vector
f.open("PS.txt", ios::binary);
while (!f.eof()) {
static PS temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(PS));
Component* x = &temp;
items.push_back(x);
}
items.pop_back();
f.close();
// read SSD objects into vector
f.open("SSD.txt", ios::binary);
while (!f.eof()) {
static SSD temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(SSD));
Component* x = &temp;
items.push_back(x);
}
// read RAM objects into vector
items.pop_back();
f.close();
f.open("RAM.txt", ios::binary);
while (!f.eof()) {
static RAM temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(RAM));
Component* x = &temp;
items.push_back(x);
}
items.pop_back();
f.close();
// read case objects into vector
f.open("Case.txt", ios::binary);
while (!f.eof()) {
static Case temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(Case));
Component* x = &temp;
items.push_back(x);
}
items.pop_back();
f.close();
return items;
}
#ifndef COMPONENT_H
#define COMPONENT_H
#define SIZE 50

class Component {
protected:
int stock;
float price;
char name[SIZE];
char manufacturer[SIZE];
bool laptop;
public:
Component();
~Component();
void setManufacturer(const char*);
void setName(const char *);
void setPrice(float);
void setStock(int);
void setLaptop(bool);
virtual void printSpecs() = 0; // the pure virtual function
const char * getManufacturer() ;
const char * getName() ;
float getPrice() const;
int getStock() const;
bool getLaptop() const;
virtual int rank() = 0;
};
#endif

// One of the derived classes
void CPU::printSpecs() {
cout << "The specifications of this CPU are:n"
<< "Name: " << getName() << endl
<< "Price: EUR " << price << endl
<< "Laptop: " << ((laptop == true) ? "yes" : "no" )<< endl
<< "Stock: " << stock << endl
<< "Manufacturer: " << getManufacturer() << endl
<< "Speed: " << speed << "GHz" << endl
<< "Cores: " << cores << endl;
}

显示的代码中存在多个基本错误。这些基本错误重复多次,这只是一次出现:

while (!f.eof()) {
static CPU temp;
f.read(reinterpret_cast<char*>(&temp), sizeof(CPU));
Component* x = &temp;
items.push_back(x);
}

这里至少有三个致命的错误。

1(while (!f.eof())在这样使用时总是一个错误。

2(在此循环的每次迭代中,这将覆盖静态temp变量,并且每次指向同一确切对象的相同指针被推送到向量中时。最后,您最终会得到指向向量中同一对象的同一指针的多个副本,该指针包含从文件中读取的最后一条记录。

3(除了它实际上没有"从文件中读取的最后一条记录",因为即使是那部分也从根本上被破坏了。从上下文中可以清楚地看出,CPU不是POD.尽管您的代码无法满足最小可重现示例的要求,但从您的描述中可以清楚地看出它至少有一个虚函数。这意味着它的内容不能只是从文件中读取或写入,就像这样的二进制 blob 一样。C++不是这样工作的。这只能通过POD的类来完成,而这个不是。

要修复显示的代码,您必须修复至少三个不同的东西。

1( 正确检查正在读取的文件的结尾。

2(不要每次都覆盖相同的对象,并且每次都向矢量添加相同的指针。

3(不要使用fread从文件中读取整个内容(并且,大概首先将其写出到文件中(。当类未POD时,您无法在C++中执行此操作。您必须实现一些其他不同的方法来从文件中读取和写入数据。

PS:我还首先在显示的代码将对象写入文件的方式中看到另一个基本错误,这也值得指出。指向基类的指针不能通过reinterpret_cast转换为派生类,并且期望获得任何有意义的内容。这不是reinterpret_cast的工作方式,最终结果毫无意义,所以这将是要解决的第 4 个问题。尽管dynamic_cast会更好,但在这种情况下,为了解决上述大多数问题,这里似乎更合适的方法是实现离散虚函数,以正确地读取和写入每个派生类的内容到/从文件。

我在代码中发现的主要问题是你存储了相同的静态变量;最后一个可能是损坏的(使用f.eof()(。然后,您尝试通过调用items.pop_back()来挽救这种情况。这没有效果,因为派生类的所有对象都具有相同的潜在损坏数据。

@SamVarshavchik是绝对正确的...您可以通过更改二进制文件的结构来避免使用f.eof()

  1. 在整数变量n中存储对象数
  2. 将整数n写入文件
  3. n对象写入文件

从二进制文件读取时

  1. 从文件中读取整数n
  2. 对于n循环从文件中读取对象

我在这里提供一个简单的说明。下面显示的代码描述了一个base

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstring>
#include <list>
using namespace std;
class Person
{
protected:
char name[30];
int age;
public:
Person(void) : name{''}, age(0) { }
Person(const char *name, const int &age) : name{''}, age(age) { std::strncpy(this->name, name, 29); }
Person(const Person&) = delete;
virtual ~Person(void) { }
virtual int type(void) { return 0; }
virtual std::string to_string(void)
{
std::basic_stringstream<char> sout;
sout << "[ name: " << name << "; ]";
return sout.str();
}
};

这是一个derived

class Employee : public Person
{
private:
int id;
public:
Employee(void) : id(0) { }
Employee(const char *name, const int &age, const int &id) : Person(name, age), id(id) { }
Employee(const Employee&) = delete;
virtual ~Employee(void) { }
// @override
int type(void) { return 1; }
// @override
std::string to_string(void)
{
std::basic_stringstream<char> sout;
sout << "[ name: " << name << "; age: " << age << "; id: " << id << "; ]";
return sout.str();
}
};

下面是一个代码示例,它实际将类的对象写入/读取derived类的对象到二进制文件/从二进制文件写入/读取test.txt

int main(int argc, char** argv)
{
std::fstream file;
if (1) // write
{
file.open("test.txt", std::ios_base::out|std::ios_base::binary);
if (file.is_open() == 0) { cout << "fail 1..."; return 0; }
Employee e1("james bond", 19, 10007);
Employee e2("harry porter", 25, 10011);
int count = 2;
file.write(reinterpret_cast<char*>(&count), sizeof(int));
file.write(reinterpret_cast<char*>(&e1), sizeof(Employee));
file.write(reinterpret_cast<char*>(&e2), sizeof(Employee));
file.close();
}
std::list<Person*> persons;
if (1) // read
{
file.open("test.txt", std::ios_base::in|std::ios_base::binary);
if (file.is_open() == 0) { cout << "fail 2..."; return 0; }
int count = 0;
file.read(reinterpret_cast<char*>(&count), sizeof(int));
while ((count--) > 0)
{
auto *e = new Employee();
file.read(reinterpret_cast<char*>(e), sizeof(Employee));
persons.push_back(e);
}
file.close();
}
for (auto *ptr : persons) { cout << ptr->to_string() << 'n'; delete ptr; }
persons.clear();
cout << "done..n";
return 0;
}

这是输出

[ name: james bond; age: 19; id: 10007; ]
[ name: harry porter; age: 25; id: 10011; ]
done..

最新更新