【问题标题】:Why I cannot access dynamic allocated memory in my for loop?为什么我无法在我的 for 循环中访问动态分配的内存?
【发布时间】:2015-02-26 21:23:50
【问题描述】:

我为从基类仪器继承的子类类型股票新建了一个内存,当我尝试访问数组的第二个元素时,它会引发错误。当我的新数组大小为 1 时,一切都很好

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

class Instrument{

public:
    virtual void display(){}
    virtual void output(){}
    virtual void readFile(){}
    virtual ~Instrument(){}
};
class Stock : 
    public Instrument{
public:
    Stock(){

    }
    virtual void input(){
        cout << "This is stock, please input its information: ";
        cin >> name >> bidPrice >> askPrice >> lastPrice >> issueExchange;
    }
    virtual void display(){
        cout <<"This is to display stock: "<< name << " "
            << bidPrice << " "
            << askPrice << " "
            << lastPrice << " "
            << issueExchange << " "
            << endl;
    }
    virtual void output(){
        ofstream myfile;
        myfile.open("Stock.txt", ios::out | ios::app);
        if (myfile.is_open()){
            myfile << "This is a stock: "
                << name << " "
                << bidPrice << " "
                << askPrice << " "
                << lastPrice << " "
                << issueExchange << " "
                << endl;
        }
        else cout << "Unable to open file";
    }
    virtual void readFile(){
        string line;
        ifstream myfile("Stock.txt");
        cout << "\nThis is file stored\n";
        if (myfile.is_open())
        {
            while (getline(myfile, line))
            {
                cout << line << '\n';
            }
            myfile.close();
        }
    }
    virtual ~Stock(){}
private:
    char name[13];
    double bidPrice;
    double askPrice;
    double lastPrice;
    int issueExchange;

};


int main(){

    const int N = 5;//it works fine if I use N=1;
    Instrument *pBase = NULL;
    pBase = new Stock[N];

    for (int i = 0; i < N; i++){
        pBase[i].input();// here throws an exception and ends the program
        pBase[i].display();
        pBase[i].output();
    }
    pBase[N - 1].readFile();
    delete[] pBase;

    system("pause");
    return 0;

} 

【问题讨论】:

  • @GermaineJason 这不是 Java 或 C#。有对象,不存在 NPE。
  • 有趣,我看到了这个错误,并且很惊讶我以前从未见过这个。这几乎就像切片,但又有所不同。 @GermaineJason 数组中有 5 个对象。
  • @MooingDuck,它从字符串变成了char[13],你看到我的#include
  • 问题是你正在访问一个Stock 数组,就好像它是一个Instrument 数组一样。多态性在整个数组之间不起作用,您需要一个指向每个单独元素的指针。
  • @Galik:请不要在评论区回答问题。这就是 answer 部分的用途。谢谢。

标签: c++ pointers polymorphism


【解决方案1】:

多态性和指针运算不能混合使用,因为数组中对象的排列取决于派生最多的大小,而多态性会丢失该信息。动态分配是一个红鲱鱼,你可以看到同样的问题:

Derived array[2];
Base* p = array;

printf("%p\n", &array[0]);
printf("%p\n", p);
printf("%p\n", &array[1]);
printf("%p\n", p + 1);

printf("%z\n", sizeof (array[0]));
printf("%z\n", sizeof (*p));

请注意,使用array 的指针值向前移动sizeof (Derived),但使用p 的指针运算向前移动sizeof (Base),并没有找到真实的对象。

通常您会使用Base* 的数组来解决此问题,而不是使用单个Base* 结合指针算法。

Base* pp[2];
for( auto& elem : array ) pp[&elem - array] = &elem;

printf("%p\n", &array[1]);
printf("%p\n", pp[1]);

// use (*pp[1]) or pp[1]->whatever

另一种选择是使用记住原始类型的对象:

Derived* allocated = new Derived[N];
std::function<Base& (int)> poly = [allocated](int i){ return allocated[i]; };

并使用poly(i) 而不是p[i]

但是警告,你不能这样做 delete [] &amp;poly(0); 因为 delete[] 也不是多态的。

使用std::unique_ptr&lt;Derived[]&gt;std::bind,可以在访问器对象最终超出范围时安排自动解除分配。

【讨论】:

  • 虽然准确,但这个答案有点缺乏解释,不是吗?
  • 是的,这就是重点,但我的任务要求我同时使用多态性和数组,我尝试使用 intrument *p[N],并访问 p[i],但确实如此不在这里工作。这是我尝试过的新方法
  • @Leewis 您可以使用例如。指向单个对象的指针数组(无论何种类型),而不是对象数组本身。
  • 是的,我在 SO 的初学者代码中经常看到这种模式。不过我不喜欢。 nnew 并且,例如,没有内存局部性 :(
  • @LightnessRacesinOrbit:您还可以使用指针数组,每个指针指向数组中的相应元素。
【解决方案2】:

虽然Ben先生的方法是绝对正确的,但是我完全觉得他的C++和我的C++不是同一种语言,他在这里混合了一些奇怪的东西,所以按照他的想法,我尝试这样修改我的代码.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

class Instrument{

public:
    virtual void display() = 0;
    virtual void output() = 0;
    virtual void readFile() = 0;
    virtual ~Instrument(){};
};
class Stock :
    public Instrument{
public:
    Stock(){
        cout << "This is stock, please input its information: ";
        cin >> name >> bidPrice >> askPrice >> lastPrice >> issueExchange;
    }
    virtual void display(){
        cout << "This is to display stock: " << name << " "
            << bidPrice << " "
            << askPrice << " "
            << lastPrice << " "
            << issueExchange << " "
            << endl;
    }
    virtual void output(){
        ofstream myfile;
        myfile.open("Stock.txt", ios::out | ios::app);
        if (myfile.is_open()){
            myfile << "This is a stock: "
                << name << " "
                << bidPrice << " "
                << askPrice << " "
                << lastPrice << " "
                << issueExchange << " "
                << endl;
        }
        else cout << "Unable to open file";
    }
    virtual void readFile(){
        string line;
        ifstream myfile("Stock.txt");
        cout << "\nThis is file stored\n";
        if (myfile.is_open())
        {
            while (getline(myfile, line))
            {
                cout << line << '\n';
            }
            myfile.close();
        }
    }
    virtual ~Stock(){}
private:
    string name;
    double bidPrice;
    double askPrice;
    double lastPrice;
    int issueExchange;

};
class Option :
    public Instrument{
public:
    Option(){
        cout << "This is option, please input its information: ";
        cin >> name >> uname >> bidPrice >> askPrice >> lastPrice >> contractSize >> exp;
    }
    virtual void display(){
        cout << "This is to display option: "
            << name << " "
            << uname << " "
            << bidPrice << " "
            << askPrice << " "
            << lastPrice << " "
            << contractSize << " "
            << exp << " "
            << endl;
    }
    virtual void output(){
        ofstream myfile;
        myfile.open("Option.txt", ios::out | ios::app);
        if (myfile.is_open()){
            myfile << "This is an option: "
                << name << " "
                << uname << " "
                << bidPrice << " "
                << askPrice << " "
                << lastPrice << " "
                << contractSize << " "
                << exp << " "
                << endl;
        }
        else cout << "Unable to open file";
    }
    virtual void readFile(){
        string line;
        ifstream myfile("Option.txt");
        cout << "\nThis is file stored\n";
        if (myfile.is_open())
        {
            while (getline(myfile, line))
            {
                cout << line << '\n';
            }
            myfile.close();
        }
    }
    virtual ~Option(){}
private:
    string name;
    string uname;
    double bidPrice;
    double askPrice;
    double lastPrice;
    int contractSize;
    double exp;
};
class Future :
    public Instrument{
public:
    Future(){
        cout << "This is option, please input its information: ";
        cin >> name >> uname >> bidPrice >> askPrice >> lastPrice >> contractSize >> tickSize >> contractMonth;
    }
    virtual void display(){
        cout << "This is to display option: "
            << name << " "
            << uname << " "
            << bidPrice << " "
            << askPrice << " "
            << lastPrice << " "
            << contractSize << " "
            << tickSize << " "
            << contractMonth << " "
            << endl;
    }
    virtual void output(){
        ofstream myfile;
        myfile.open("Future.txt", ios::out | ios::app);
        if (myfile.is_open()){
            myfile << "This is a future: "
                << name << " "
                << uname << " "
                << bidPrice << " "
                << askPrice << " "
                << lastPrice << " "
                << contractSize << " "
                << tickSize << " "
                << contractMonth << " "
                << endl;
        }
        else cout << "Unable to open file";
    }
    virtual void readFile(){
        string line;
        ifstream myfile("Future.txt");
        cout << "\nThis is file stored\n";
        if (myfile.is_open())
        {
            while (getline(myfile, line))
            {
                cout << line << '\n';
            }
            myfile.close();
        }
    }
    virtual ~Future(){}
private:
    string name;
    string uname;
    double bidPrice;
    double askPrice;
    double lastPrice;
    int contractSize;
    int tickSize;
    int contractMonth;
};

int main(){

    int N = 20;
    //shared_ptr<Instrument> pBase[N];
    vector<shared_ptr<Instrument>> pBase(N);
    int i = 5;
    for (i = 0; i < N; i++) pBase[i] = make_shared<Stock>();
    for (i = 0; i < N; i++){
        pBase[i]->display();
        pBase[i]->output();
    }
    pBase[N - 1]->readFile();

    for (i = 0; i < N; i++) pBase[i] = make_shared<Option>();
    for (i = 0; i < N; i++){
        pBase[i]->display();
        pBase[i]->output();
    }
    pBase[N - 1]->readFile();

    for (i = 0; i < N; i++) pBase[i] = make_shared<Future>();
    for (i = 0; i < N; i++){
        pBase[i]->display();
        pBase[i]->output();
    }
    pBase[N - 1]->readFile();

    system("pause");
    return 0;

}

【讨论】:

    【解决方案3】:

    tl;dr 回答:不要将子类数组 (Stock[N]) 转换为基类指针 (pBase)。要么直接使用Stock*,要么创建一个指针数组:

    auto arr = new Stock*[N];
    for (int i = 0; i < N; i++) {
      arr[i] = new Stock();
    }
    
    // unrelated but suggested: C++11 unique_ptr is recommended:
    vector<unique_ptr<Stock>> v(N);
    

    详细原因:

    1) 数组括号运算符是语法糖:a[b] -&gt; *(a + b);

    2) 指针算法不是多态的,它总是基于静态类型:

    pBase[i] -> *(pBase+i) -> *(pBase*)((char*)pBase + sizeof(Instrument) * i);
    

    3) 然而,这正是你想要的:

    pBase[i] -> *(pBase+i) -> *(pBase*)((char*)pBase + sizeof(Stock) * i);
    

    4) 只要sizeof(Instrument) != sizeof(Stock),你就有麻烦了。

    【讨论】:

    • 我愿意,看我的回答,vector> pBase(N);
    猜你喜欢
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 2018-10-11
    • 2012-12-20
    相关资源
    最近更新 更多