当前位置: 首页 > 知识库问答 >
问题:

不知道如何让g接受我的代码

连时铭
2023-03-14

我已经调试这段代码好几个小时了,试图让输出正确无误。它在早期工作,但输出中存在逻辑错误,因此我进入并在输出函数中添加了循环和一个额外参数。

现在g给我以下错误:

Student.cpp:在成员函数“void Student::input data(std::string,int,STD::string

如何修复此代码?:

//This program defines a class for storing the names of classes that a student has enrolled in.

#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;

class Student
{
public:
    Student();
    Student(Student& obj); // copy constructor
    ~Student();
    void InputData(string,int,string&);     // Input all data from user
    void OutputData();      // Output class list to console
    void ResetClasses();        // Reset class list
    Student& operator =(const Student& rightSide){
    this->name=rightSide.name;
    this->numClasses=rightSide.numClasses;
    this->classList=rightSide.classList;
}
    // Assignment operator

private:
    string name;
    int numClasses;
    string *classList;
};

// --------------------------------
// ----- ENTER YOUR CODE HERE -----
// --------------------------------

// ======================
// Student::Student
// The default constructor initialized variables to empty.
// The dynamic array is intialized to NULL.
// ======================
Student::Student () {
        name="";
    numClasses=0;
    classList=NULL;


}
// ======================
// Student::Student
// The copy constructor creates a deep copy of the parameter object
// ======================
Student::Student (Student& obj) {
    obj.name=name;
    obj.numClasses=numClasses;
    obj.classList=classList;
}   
// ======================
// Student::~Student
// The destructor frees up any memory allocated to
// the dynamic array.
// ======================
Student::~Student () {
    delete classList;
}
// ======================
// Student::ResetClasses
// This method deletes the class list
// ======================
void Student::ResetClasses () {
    if(classList) { delete [] classList;}
}
// ======================
// Student::InputData
// This method inputs all data from the user. 
// It allows the user to enter an arbitrary number of classes
// using a dynamic array to store the strings.
void Student::InputData(string nm, int nClasses, string& names) {
    name=nm;
    numClasses=nClasses;
    delete classList;
    for (int i=0; i<nClasses; i++) {
        names=new string[i];
    }
}   

// Reset the class list before entering data just in case this method
// was called repeatedly and the dynamic array wasn't cleared
// ======================


// ======================
// Student::OutputData
// This method outputs the data entered by the user.
// ======================
void Student::OutputData() {
    cout << "Student name : " << name <<endl;
    cout << "Student number of classes : " << numClasses <<endl;
    cout << "Student class list : " <<classList<<endl;
}

// ======================
// Student::=

// operator, we would end up with two references to the same
// class list when the assignment operator is used.
// ======================
//
// --------------------------------
// --------- END USER CODE --------
// --------------------------------



// ======================
//     main function
// ======================
int main()
{
  // Test our code with two student classes
  Student s1, s2;

  string sname;
  int snumClasses;
  string snames[]="";

  cout << "Enter student name, number of classes, and names of classes for first student" << endl;
  cin >> sname; cin >> snumClasses; 

  int i; 
  for (i=0; i < snumClasses; i++) {
    cin >> snames[i];
  }

  s1.InputData(sname, snumClasses, snames[i]);      // Input data for student 1
  cout << "Student 1's data:" << endl;
  s1.OutputData();      // Output data for student 1

  cout << endl;

  s2 = s1;  
  cout << "Student 2's data after assignment from student 1:" << endl;
  s2.OutputData();      // Should output same data as for student 1

  s1.ResetClasses();
  cout << "Student 1's data after reset:" << endl;
  s1.OutputData();      // Should have no classes

  cout << "Student 2's data, should still have original classes:" << endl;
  s2.OutputData();      // Should still have original classes

  Student s3(s2);  // explicit copy constructor call
  cout << "Student 3's data after assignment from student 2:" << endl;
  s2.OutputData(); // should have the same classes as student 2

  cout << endl;
  return 0;
}

共有1个答案

梁晋鹏
2023-03-14

太长别读--http://ideone.com/rTVQUo

第一个也是最重要的问题是:

string snames[] = "";

这是声明一个只包含一个元素的字符串数组。如果 snumClasses 大于 1,这将使以下代码成为未定义的行为:

for (i = 0; i < snumClasses; i++) {
    cin >> snames[i];
}

上面的代码将html" target="_blank">调用未定义的行为,因为如果<code>i

要解决此问题,sname的大小应该是用户指定的大小。特别是,sname应该是指向大小为snumClass的动态分配数组的指针:

std::string* snames;
std::cin >> snumClasses;

snames = new std::string[snumClasses];

注意:稍后我会解释为什么不应该这样做。

当数据使用完毕时,请指定< code>delete[]它:

delete[] snames;

下一个问题来自这一行:

s1.InputData(sname, snumClasses, snames[i]);
//                               ^^^^^^^^^

学生对象有三个数据成员,类的名称、类的数量和类的数组。InputData成员函数用于将这些数据成员分配给给定的参数。您已经为前两个参数给出了正确的参数(因为string可以分配给string并且int可以分配给int),但您的第三个参数与数据成员的类型不匹配。对于您来说,发送数组sname而不是您通过执行sname[i]所做的其中的元素是有意义的。

s1.InputData(sname, snumClasses, snames);
//                               ^^^^^^

这也需要< code>InputData接受< code>string*而不是< code>string

void Student::InputData(string nm, int nClasses, string* names)
{
    name = nm;
    numClasses = nClasses;

    delete[] classList;
    classList = names;                                                         /*
    ^^^^^^^^^^^^^^^^^^                                                         */
}

现在我已经减少了您的大部分错误,让我们继续改进您的程序。

我将从主要的明显问题开始,并偏离更次要(但重要)的设计问题。

Student::Student(Student& obj)
{
    obj.name       = name;
    obj.numClasses = numClasses;
    obj.classList  = classList;
}

此构造函数不仅更像赋值运算符,而且您已经切换了成员的赋值。您应该将 *this 的数据成员分配给正在复制的对象,而不是相反。此外,您的复制构造函数必须对 const 进行引用:

Student::Student(const Student& obj)
{
    name       = obj.name;
    numClasses = obj.numClasses;
    classList  = obj.classList;
}

请注意,考虑到< code>*this和正在复制的对象之间的生存期依赖关系,需要执行深度复制。

Student::Student(const Student& obj)
{
    name       = obj.name;
    numClasses = obj.numClasses;
    std::copy(obj.classList, obj.classList + numClasses, classList);
}

这个构造函数还有一个问题是它使用赋值而不是初始化。您的类的数据成员应该使用成员初始化器列表进行初始化:

Student::Student(const Student& obj)
    : name(obj.name),
      numClasses(obj.numClasses),
      classList(numClasses ? new std::string[numClasses]{} : nullptr)
{
    std::copy(obj.classList, obj.classList + numClasses, classList);
}
Student& operator=(const Student& rightSide)
{
    this->name       = rightSide.name;
    this->numClasses = rightSide.numClasses;
    this->classList  = rightSide.classList;
}

此运算符与复制构造函数存在相同的问题,因为它考虑内存管理。如果我们从*将此分配到右侧,我们必须删除当前的类列表对象,并从复制到该对象的对象中复制元素。如果我们不这样做,我们将面临内存泄漏的风险:

Student& operator=(const Student& rhs)
{
    name       = rhs.name;
    numClasses = rhs.numClasses;

    delete[] classList;  // delete classList
    classList = nullptr; // set to nullptr because if 'new' throws, we can still
                         // delete without causing undefined behavior

    classList = new std::string[numClasses]{};
    std::copy(rhs.classList, rhs.classList + numClasses, classList);

    return *this; // you forgot to return the object!
}

这看起来恰到好处,但也可以改进。就目前而言,此代码不提供强异常保证。如果出现新的抛出,则 *this 的状态将与它进入赋值运算符时的状态不同。为了规避这一点,我们可以应用复制和交换习语,以提供强异常保证:

Student& operator=(Student rhs)
{
    swap(*this, rhs);
    return *this;
}

该参数已更改为按值取值,因此当传入左值时,我们可以复制它并交换其数据成员。函数<code>swap</code>封装了交换语义。它在学生中定义如下:

friend void swap(Student& first, Student& second)
{
    using std::swap;
    swap(first.name, second.name);
    swap(first.numClasses, second.numClasses);
    swap(first.classList, second.classList);
}

有关更多信息,请参见什么是复制和交换习惯用法?

每当您想使用新的时,请考虑标准库中给出的替代项。它们实现 RAII,因此不需要用户自己释放内存。这是非常有利的,因为它可以减少潜在的内存泄漏并保持代码的干净和高效。我们可以利用标准库容器 std::vector

private:
    std::string name;
    std::vector<std::string> classList;

请注意,我还删除了 numClasses 数据成员。我们不需要它,因为向量有一个size()成员函数。此外,由于不需要内存管理,我们可以实现零法则,而不是实现默认 ctor、copy-ctor 和析构函数!为什么?因为默认情况下编译器将生成这些成员函数。

您可以通过定义自己的插入器/提取器来减少大量代码。以下是这两个的实现:

std::ostream& operator<<(std::ostream& os, const Student& s)
{
    s.OutputData(os);
    return os;
}

std::istream& operator>>(std::istream& is, Student& s)
{
    s.classList.assign(std::istream_iterator<std::string>{is >> s.name},
                       std::istream_iterator<std::string>{});

    return is;
}

这就是在<code>main()中使用它的方法。<code>:

int main()
{
    Student s1, s2;

    if (std::cin >> s1 >> s2)
         std::cout << s1 << s2;
}

这应该是您的类的结构:

class Student
{
public:
    void InputData(std::string, const std::vector<std::string>&);
    void OutputData(std::ostream&) const;
    void ResetClasses();

    friend std::ostream& operator<<(std::ostream&, const Student&);
    friend std::istream& operator>>(std::istream&,       Student&);
private:
    std::string name;
    std::vector<std::string> numClasses;
};

这是这些成员函数的实现:

void Student::InputData(std::string nm, const std::vector<std::string>& names)
{
    name = nm;
    numClasses = names;
}

void Student::OutputData(std::ostream& os) const
{
    os << "Students name: " << name << std::endl;
    os << "Number of classes: " << classList.size() << std::endl;
    os << "Classes: ";

    std::copy(classList.begin(), classList.end(),
        std::ostream_iterator<std::string>(os, "\n"));
}

void Student::ResetClasses()
{
    classList.clear();
}

 类似资料:
  • 嗨,我发现了这个使用注释的pdfbox示例: https://svn.apache.org/repos/asf/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/addannotations.java 当我尝试在netbeans中运行它时,除了在输出中打印之外,什么也没有发生:

  • 我拿不到输出。。有人能帮我得到输出吗 下面给出了程序运行的示例(注意:下面的粗体文本是用户输入的输入): 输入三角形的三条边

  • 当我尝试编译这段代码时,Eclipse会出现以下两个错误: > 用于函数:maxmin的非法修饰符;只允许最终 对于:Mn不能解析为变量 为什么会出现这两个错误? 我想这可能已经回答了这个问题,但我不明白其中使用的术语。 这是我的代码:

  • 我遇到了一些问题,AppEngine抱怨我的代码有Java1.8方法,而AppEngine需要Java1.7。下面是错误跟踪。我不知道该怎么修理它。

  • 我部署了几个不同的docker容器,mysql是第一个。我想尽快运行脚本数据库是和继续建设其他容器。该脚本一直失败,因为当设置mysql(从这个官方mysql容器)的entrypoint脚本仍在运行时,它正试图运行。 有没有办法等待入口点mysql安装脚本在docker容器内完成的信号?Bash睡眠似乎是一个次优的解决方案。 编辑:像这样的bash脚本。不是最优雅和有点蛮力,但工作像魅力。也许有人

  • 该程序允许用户以年数输入贷款金额和贷款期限。 每月和总付款应以1/8的利率递增显示。到目前为止,我已经足够正确地计算出一个数量,但我不知道如何显示所有结果。 我拍摄了以下问题的说明: http://imgur.com/s9JEbtu