写访问冲突-**此**

时间:2018-11-06 03:33:48

标签: c++ visual-studio

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

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

再进行一次编辑:输入文件在我以前没有指针的情况下工作得很好,但是当我添加指针的那一刻,一切都崩溃了。

这是我的代码: 在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";
}

我遇到的错误是引发异常:写访问冲突。 这个是0xCCCCCCCCCC,它在创建于的断点处引发了异常

在第firstName = studentFirstName行处的void Student :: setFirstName(字符串studentFirstName)。

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

2 个答案:

答案 0 :(得分:1)

让我们对其进行调试:

由于您没有提供完整的测试用例,因此我将学生数更改为3,并将测试数更改为0:

  

student_records.dat

1
Foo
Bar
2
Foo2
Bar2
3
Foo3
Bar3

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

call stack

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

studentsPtr[index]->setID(tempID);

首先,让我们看一下变量:

variables

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

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

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

studentsPtr设置为指向学生的指针。内部指针实际上是一组学生。但是,外部指针是一个单独的Student*。当像studentsPtr[1]那样对其进行索引时,我们将超出其中的单个指针,并继续踩到不存在的Student*。然后,我们尝试编写该程序,该程序很早就崩溃了。

解决方案是摆脱双指针。所需要的只是一堆Student。一个指针是一种(不推荐的表示方式)数组:

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

现在,由于在编译时已知元素的数量,因此建议的类型将为std::arraystd::array<Student, numStudents> students;),在声明之后,可以使用与上面相同的语法。如果在编译时不知道大小,则建议的类型为std::vector,它也使用相同的语法来访问元素。

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

(&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简化了输入文件。总之,调试器提供了可以完成调试工作的工具。仅从调试器开始,我们就将问题简化为指向仅一件事而不是多件事的双指针。与原始问题相比,该问题的范围要小得多。

答案 1 :(得分:1)

  

我做错什么了吗?

是的,绝对是:)

我们来看一下:

Student

...上面分配了15个Student ** studentsPtr = &students; 对象的动态数组;到目前为止,一切都很好。

Student **

此行是问题的根源。您已经声明了双指针students,并将其初始化为指向students指针的地址。这是合法的C ++,但请注意,只有独立的pointers-to-Student指针-特别是,程序中的任何地方都没有Student数组。 (有pointers-to-Student对象数组,但这与for (int index = 0; index < numStudents; index++) { [...] studentsPtr[index]->setID(tempID); // BOOM! 的数组不同。)

...再过一会儿,就会遇到实际的麻烦:

studentsPtr

在这里,您尝试使用pointers-to-Student,就好像它是index数组的基址一样,即通过用students指针偏移其位置并取消引用位置。但这并不是真正指向指针数组,而是指向单个指针(即,它指向变量index),因此只要Microsoft Windows [Version 6.3.9600] (c) 2013 Microsoft Corporation. All rights reserved. C:\Users\User>pip install pygame Collecting pygame Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFI CATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certif icate (_ssl.c:1045)'))': /simple/pygame/ Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFI CATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certif icate (_ssl.c:1045)'))': /simple/pygame/ Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFI CATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certif icate (_ssl.c:1045)'))': /simple/pygame/ Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFI CATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certif icate (_ssl.c:1045)'))': /simple/pygame/ Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFI CATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certif icate (_ssl.c:1045)'))': /simple/pygame/ Could not fetch URL https://pypi.org/simple/pygame/: There was a problem confi rming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max r etries exceeded with url: /simple/pygame/ (Caused by SSLError(SSLCertVerificatio nError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1045)'))) - skipping Could not find a version that satisfies the requirement pygame (from versions: ) No matching distribution found for pygame C:\Users\User> 为非零,就表示您正在调用未定义的行为,因此(在您的情况下)会导致崩溃。