简体   繁体   English

无法弄清楚如何使G ++接受我的代码

[英]Cannot figure out how to get g++ to accept my code

I've been debugging this code for hours trying to get the output correct and g++ error free. 我一直在调试该代码数小时,以尝试获得正确的输出和g ++错误。 It was working earlier but there were logic errors in the output so then I went in and added the loop and an extra parameter in the output function. 它工作较早,但是输出中存在逻辑错误,因此我进入并在输出函数中添加了循环和一个额外的参数。

Now g++ gives me the following error: 现在,g ++给了我以下错误:

Student.cpp: In member function 'void Student::InputData(std::string, int, std::string&)': Student.cpp:81:21: error: invalid conversion from 'std::string* {aka std::basic_string*}' to 'char' [-fpermissive] /usr/include/c++/4.6/bits/basic_string.h:560:7: error: initializing argument 1 of 'std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits, _Alloc = std::allocator, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string]' [-fpermissive] Student.cpp:在成员函数'void Student :: InputData(std :: string,int,std :: string&)':Student.cpp:81:21:错误:从'std :: string *无效的转换{aka std :: basic_string *}'转换为'char'[-fpermissive] /usr/include/c++/4.6/bits/basic_string.h:560:7:错误:初始化'std :: basic_string <_CharT,_Traits,_Alloc的参数1 >&std :: basic_string <_CharT,_Traits,_Alloc> :: operator =(_ CharT)[with _CharT = char,_Traits = std :: char_traits,_Alloc = std :: allocator,std :: basic_string <_CharT,_Traits,_Alloc > = std :: basic_string]'[-fpermissive]

How can this code be fixed?: 该代码如何固定?

//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;
}

TL;DR -- http://ideone.com/rTVQUo TL; DR- http://ideone.com/rTVQUo


The first and foremost issue is this: 第一个也是最重要的问题是:

string snames[] = "";

This is declaring a array of strings that hold only one element. 这是在声明仅包含一个元素的字符串数组。 That will make the following code undefined behavior if snumClasses is greater than 1: 如果snumClasses大于1,将使以下代码未定义行为:

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

The above code would invoke undefined behavior because you would be accessing out-of-bound addresses if i grows greater than one. 上面的代码将调用未定义的行为,因为如果i大于1,您将访问出站地址。 Once Undefined Behavior hits your program, you can't make sense of any behavior that ensues because of this. 一旦“未定义行为”进入您的程序,您将因此而无法理解随后发生的任何行为。 Your program could break, your computer could shut down or worse - nasal demons. 您的程序可能会中断,计算机可能会关闭或变得更糟-鼻恶魔。

To fix this, the size of snames should be the size that the user specifies. 为了解决这个问题,大小snames应该是用户指定的大小。 In particular, snames should be pointer to a dynamically-allocated array whose size is snumClasses : 特别是, snames应该是指向大小为snumClasses的动态分配数组的snumClasses

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

snames = new std::string[snumClasses];

Note: I'll explain later on why this should not be done. 注意:稍后我将解释为什么应该这样做。

And when the data is finished being used, make it a point to delete[] it: 当数据使用完毕后,将其指向delete[]

delete[] snames;

The next problem is coming from this line: 下一个问题来自此行:

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

A Student object has three data members, the name of the class, the number of classes, and the array of classes. Student对象具有三个数据成员,即类的名称,类的数量和类的数组。 The InputData member function is used for assigning those data members to the arguments given. InputData成员函数用于将这些数据成员分配给给定的参数。 You've given the correct arguments for the first two parameters (because an string can be assigned to an string and an int can be assigned to an int ) but your third argument does not match the data member's type. 您已经为前两个参数提供了正确的参数(因为可以将string分配给string并且可以将int分配给int ),但是第三个参数与数据成员的类型不匹配。 It would make sense for you to be sending the array snames instead of the element within it which you have done by doing snames[i] . 对于您来说,发送数组snames而不是发送数组中的元素是snames而这是通过执行snames[i]

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

This also necessitates that InputData take a string* rather than a string& and that the classList pointer simply be assigned to the new snames array: 这也需要InputData接受string*而不是string& ,并且classList指针只需分配给新的snames数组即可:

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

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

Now that I've reduced the bulk of your errors, let's move on to improving your program. 现在,我已减少了大部分错误,让我们继续改进您的程序。

I'll start from major apparent issues and deviate towards more minor (but important) design issues. 我将从明显的主要问题开始,然后转向较小的(但很重要)设计问题。

The copy-constructor: 复制构造函数:

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

Not only does this constructor work more like an assignment-operator , but you've switch up the assignment of the members. 这个构造函数不仅更像是一个赋值运算符 ,而且您已经切换了成员的赋值。 You're suppose to assign the data members of *this to the object being copied, not the other way around. 您假设将*this的数据成员分配给要复制的对象,而不是相反。 Also, your copy-constructor has to take a reference to const : 另外,您的复制构造函数必须引用const

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

Note that a deep copy needs to be performed considering lifetime dependencies between *this and the object that's being copied. 请注意,需要执行深度复制,要考虑*this和要复制的对象之间的生命周期依赖性。

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

One more thing wrong with this constructor is that it uses assignment instead of initialization . 此构造函数的另一件事是,它使用赋值而不是初始化 The data members of your class should be initialized using the member-initializer list : 您的类的数据成员应使用member-initializer列表进行初始化

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);
}

The assignment operator: 赋值运算符:

Student& operator=(const Student& rightSide)
{
    this->name       = rightSide.name;
    this->numClasses = rightSide.numClasses;
    this->classList  = rightSide.classList;
}

This operator has the same problem as the copy-constructor as it does consider memory management. 此运算符与复制构造函数存在相同的问题,因为它确实考虑了内存管理。 If we are assigning from *this to rightSide , we have to delete[] the current classList object and copy the elements from the object being copied thereto. 如果要从*this分配给rightSide ,则必须delete[]当前的classList对象,并从要复制到其中的对象中复制元素。 If we don't do this, we risk leaking memory: 如果我们不这样做,则可能会导致内存泄漏:

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 looks just about right, but it can also be improved. 这看起来差不多正确,但是也可以进行改进。 As it stands this code does not provide a strong-exception guarantee. 就目前而言,此代码不提供强异常保证。 If new throws, the state of *this will be different from when it entered into the assignment operator. 如果有new抛出, *this的状态将不同于它进入赋值运算符时的状态。 To circumvent this we can apply the copy-and-swap idiom in order to provided a strong-exception guarantee: 为了避免这种情况,我们可以应用复制和交换惯用语来提供强例外保证:

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

The parameter has been changed to take by value so that when an lvalue is passed in we can copy it and swap its data members. 该参数已更改为按值使用,以便在传递左值时我们可以复制它并交换其数据成员。 The function swap encapsulates the swapping semantics. 函数swap封装了交换语义。 It is defined inside Student as follows: Student内部定义如下:

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);
}

For more information, see What is the copy-and-swap idiom? 有关更多信息,请参见什么是复制和交换惯用法?


Don't use new : 不要使用new

Whenever you feel like using new , consider alternatives given in the Standard Library. 每当您想使用new ,请考虑标准库中提供的替代方法。 They implement RAII and as such don't require the user to deallocate the memory themselves. 它们实现了RAII,因此不需要用户自己释放内存。 This is very advantageous as it reduces potential memory leaks and keeps your code clean and efficient. 这是非常有利的,因为它减少了潜在的内存泄漏,并使您的代码保持干净高效。 We can utilize the standard library container std::vector<T> for the list of classes and substitute it for the string* classList data member: 我们可以将标准库容器std::vector<T>用于类列表,并将其替换为string* classList数据成员:

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

Notice how I've also removed the numClasses data member. 请注意,我也是如何删除numClasses数据成员的。 We don't need it as the vector has a size() member function. 我们不需要它,因为向量具有size()成员函数。 Moreover, since there is no need for memory management, we can implement the Rule of Zero and not implement the default-ctor, copy-ctor and destructor! 而且,由于不需要内存管理,因此我们可以实现零规则,而无需实现default-ctor,copy-ctor和destructor! Why? 为什么? Because the compiler will generate those member functions by default. 因为编译器默认会生成那些成员函数。


Use user-defined inserters/extractors 使用用户定义的插入器/提取器

You can cut down on a lot of code by defining your own inserters/extractors. 您可以通过定义自己的插入器/提取器来减少大量代码。 Here would be the implementation of these two: 这是这两个的实现:

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;
}

And this is how you can use it in main() : 这就是您可以在main()使用它的方式:

int main()
{
    Student s1, s2;

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

This should be the structure of your class: 这应该是您的类的结构:

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;
};

And this is the implementation of those member functions: 这是这些成员函数的实现:

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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM