【问题标题】:Write access violation - **this**写访问冲突 - **this**
【发布时间】:2018-11-06 03:33:47
【问题描述】:

我正在尝试重构一些代码以使用指针并且在我的函数调用中遇到写访问冲突。

我进行这些编辑是因为我的家庭作业项目需要使用 -> 成员运算符以及构造函数和析构函数。

再修改:当我以前没有指针工作时,输入文件工作得很好,但是当我添加指针的那一刻,一切都坏了。

这是我的代码: 在 main.cpp 中:

#include "student.h"

int main()
{
    /*
    TODO:
    2. Implement the class such that member pointers can be used to access the members.
    3. Implement pointers that point to each of the students' test scores as well as the average test score.
    */

    const int numStudents = 15;                     // Number of students
    Student * students = new Student[numStudents];  // Dynamically allocated array of Student objects
    Student ** studentsPtr = &students;

    // Starting file stream for studentRecords.dat
    ifstream student_records("student_records.dat");

    // Error checking for file loading
    if (!student_records)
    {
        cerr << "ERROR: The record file could not be opened for reading." << endl;
        exit(1);
    }

    // Load data from file
    string current_value;
    stringstream newval;

    int tempID;
    string tempName;
    double tempTestScore;

    for (int index = 0; index < numStudents; index++)
    {
        // Store the student ID
        getline(student_records, current_value);
        newval << current_value;
        newval >> tempID;
        studentsPtr[index]->setID(tempID);
        newval.clear();

        // Store the student first name
        getline(student_records, current_value);
        newval << current_value;
        newval >> tempName;
        studentsPtr[index]->setFirstName(tempName);
        newval.clear();

        // Store the student last name
        getline(student_records, current_value);
        newval << current_value;
        newval >> tempName;
        studentsPtr[index]->setLastName(tempName);
        newval.clear();

        // Add each test score.
        for (int testScoreIndex = 0; testScoreIndex < numTests; testScoreIndex++)
        {
            getline(student_records, current_value);
            newval << current_value;
            newval >> tempTestScore;
            studentsPtr[index]->addTestScore(tempTestScore, testScoreIndex);
            newval.clear();
        }

        // Calculate the student's average
        students[index].calculateAverage();
    }

    // Print all data
    for (int index = 0; index < numStudents; index++)
    {
        studentsPtr[index]->printAll();
    }

    delete[] students;  // Free memory pointed to by students array
    students = NULL;    // Clear the memory.

    system("pause");
    return 0;
}

在 student.h 中:

#pragma once
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>

using namespace std;

const int numTests = 10;

// Student class declaration
class Student
{
private:
    // Student ID and name
    int id;
    string firstName;
    string lastName;

    // List of student test scores
    // vector<double> testScores;
    double * testScores = new double[numTests];

    // Student average test score
    double average;

public:
    Student()       // Default constructor
    {
        const int numTests = 10;

        id = 0;
        firstName = " ";
        lastName = " ";
        average = 0.0;
    }

    ~Student()  // Destructor
    {
        delete[] testScores;
    }

    void setID(int);                // Set the student ID
    void setFirstName(string);      // Set the student name
    void setLastName(string);
    void addTestScore(double, int); // Add a test score to the vector
    void calculateAverage();        // Calculate the average of all test scores

    void printAll();                // Output all data to the screen for a given student
};

在 student.cpp 中:

    #include "student.h"

// setID sets the id value.
void Student::setID(int studentID)
{
    id = studentID;
}

// setName sets the name value.
void Student::setFirstName(string studentFirstName)
{
    firstName = studentFirstName;
}

void Student::setLastName(string studentLastName)
{
    lastName = studentLastName;
}

// addTestScore adds a test score to the vector
void Student::addTestScore(double testScore, int index)
{
    testScores[index] = testScore;
    // testScores.push_back(testScore);
}

// calculateAverage adds every test score from the vector and divides them by the number of test scores in the list.
void Student::calculateAverage()
{
    double totalScores = 0.0;

    // for (double index : testScores)
    for (int index = 0; index < numTests; index++)
    {
        totalScores += testScores[index];
    }

    average = totalScores / numTests;
}

// printAll prints all the data to the screen.
void Student::printAll()
{
    cout << "=========================================================\n";
    cout << "Student ID:\t" << id << endl;
    cout << "Student Name:\t" << firstName << " " << lastName << endl;
    cout << setprecision(4) << "Average:\t" << average << endl;
    cout << "Test Scores: " << endl;

    // Printing the test scores nicely
    int scoresPerLine = 5;

    for (int i = 0; i < numTests; i++)
    {
        cout << setprecision(4) << testScores[i] << "\t";
        if ((i + 1) % scoresPerLine == 0)
        {
            cout << endl;
        }
    }

    cout << endl;
    cout << "=========================================================\n\n";
}

我得到的错误是抛出异常:写访问冲突。 this 是 0xCCCCCCCC,它在创建的断点处引发异常

void Student::setFirstName(string studentFirstName) 在 firstName = studentFirstName 行。

我的问题是:究竟是什么阻止了它的工作?难道我做错了什么?在编译所有内容之前,我没有收到任何错误,因此看起来一切正常。我也尝试在该成员函数上使用传递引用,但同样的响应也失败了。

【问题讨论】:

  • 摆脱studentsPtr。只需在任何地方使用students
  • 这很好用,但我的部分任务是我使用 -> 成员指针,我认为我必须为此使用指针变量。
  • 是的,您使用带有指针的-&gt;。但是问题中的代码在任何地方都没有使用-&gt;

标签: c++ visual-studio


【解决方案1】:

我做错了吗?

是的,当然:)

让我们来看看吧:

Student * students = new Student[numStudents];

...上面分配了15个Student对象的动态数组;到目前为止,一切顺利。

Student ** studentsPtr = &students;

这条线是麻烦的根源。您已经声明了一个双指针 Student ** 并将其初始化为指向 students 指针的地址。这是合法的 C++,但请注意,只有独立的 students 指针——特别是,在您的程序中任何地方都没有 pointers-to-Student 数组。 (有一个 Student objects 数组,但这与 pointers-to-Student 数组不同)

... 稍后,真正的麻烦来了:

for (int index = 0; index < numStudents; index++)
{
    [...]
    studentsPtr[index]->setID(tempID);   // BOOM!

在这里,您尝试使用studentsPtr,就好像它是pointers-to-Student 数组的基地址一样,即通过将其位置偏移index 指针并取消引用该位置。但它并不是真正指向指针数组,而是指向单个指针(即,它指向变量 students),所以每当 index 非零时,您正在调用未定义的行为,因此(在你的情况下)你会崩溃。

【讨论】:

  • 谢谢杰里米!所以我重构了我的代码,包括一个非动态分配的学生对象数组和一个指针数组,每个指针都明确设置为学生数组的每个索引。问题解决了。
【解决方案2】:

让我们调试一下:

由于你没有提供完整的测试用例,我把学生数改为3,测试数改为0:

student_records.dat

1
Foo
Bar
2
Foo2
Bar2
3
Foo3
Bar3

我的崩溃发生在setID,但没关系。 this是0xCCCCCCCC,是MSVC在调试模式下给未初始化数据的值。太好了,对象指针是垃圾。它从何而来?我们将向上调用堆栈查看:

这将我们带到读取输入的main 循环中的以下行:

studentsPtr[index]->setID(tempID);

首先,我们来看看变量:

我们知道这个对象是垃圾。我们可以在这里验证。我们的对象是studentsPtr[index],它显示为相同的未初始化值。我们还看到studentsPtr 本身指向正确的第一个学生。最后,index 变量的值为 1,所以我们是第二个学生。

studentsPtr[1] 具有 MSVC 为未初始化内存提供的值。为什么未初始化?让我们回到声明:

Student *students = new Student[numStudents];
Student **studentsPtr = &students;

studentsPtr 设置为指向学生的指针。内部指针实际上是一个学生数组。然而,外部指针是一个单独的Student*。当像studentsPtr[1] 那样对其进行索引时,我们超越了内部的单个指针并继续践踏到不存在的Student*。然后我们尝试对其进行写入,幸好程序早早地崩溃了。

解决办法是去掉双指针。所需要的只是一堆Students。一个指针就是一种(不推荐的表示数组的方式):

Student *students = new Student[numStudents];
...
students[index].setID(tempID);

现在由于元素的数量在编译时是已知的,因此推荐的类型是std::array (std::array&lt;Student, numStudents&gt; students;),它可以在声明后使用与上述相同的语法。如果在编译时不知道大小,推荐的类型是std::vector,它也共享相同的语法来访问元素。

从技术上讲,您也可以使用std::array 来满足-&gt; 的要求。只需获取指向元素的指针,然后使用箭头:

(&students[index])->setID(tempID);

更有可能的是,需求仍在寻找您正在执行的手动免费存储内存管理。将箭头放入其中也很容易:

(students + index)->setID(tempID);

如果你真的,真的需要双指针,即使它没有任何用处,请记住你的数组是 inner 指针,而不是外部指针:

((*students) + index)->setID(tempID);

如果您认为箭头在所有这些情况下都会妨碍可读性,那么您是对的。也许教练有一些具体的想法,但没有。

删除双指针后会发生什么?

=========================================================
Student ID:     1
Student Name:   Foo Bar
Average:        -nan(ind)
Test Scores:

=========================================================

=========================================================
Student ID:     2
Student Name:   Foo2 Bar2
Average:        -nan(ind)
Test Scores:

=========================================================

=========================================================
Student ID:     3
Student Name:   Foo3 Bar3
Average:        -nan(ind)
Test Scores:

=========================================================

成功。平均值没有意义,因为我通过将测试数量更改为 0 来简化输入文件。长话短说,调试器提供了可以完成调试工作的工具。仅从调试器中,我们将问题简化为双指针仅指向一件事而不是多件事。与原始问题相比,该问题的范围要小得多。

【讨论】:

    猜你喜欢
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 2021-02-16
    • 2017-08-23
    • 2016-05-17
    • 1970-01-01
    相关资源
    最近更新 更多