我一直在调试这段代码几个小时试图让输出正确并且g ++没有错误。 它工作得比较早,但是输出中存在逻辑错误,所以我进入并在输出函数中添加了循环和一个额外的参数。
现在g ++给了我以下错误:
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]
如何修复此代码?:
//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;
}
答案 0 :(得分:0)
TL; DR - http://ideone.com/rTVQUo
首要问题是:
string snames[] = "";
这是声明一个只包含一个元素的字符串数组。如果snumClasses
大于1:
for (i = 0; i < snumClasses; i++) {
cin >> snames[i];
}
上面的代码会调用未定义的行为,因为如果i
增长大于1,您将访问越界地址。一旦Undefined Behavior命中您的程序,您就无法理解因此而产生的任何行为。您的程序可能会中断,您的计算机可能会关闭或更糟 - 鼻腔恶魔。
要解决此问题,snames
的大小应为用户指定的大小。特别是,snames
应该是指向动态分配的数组的指针,该数组的大小为snumClasses
:
std::string* snames;
std::cin >> snumClasses;
snames = new std::string[snumClasses];
注意:我稍后会解释为什么这应该不。
当数据使用完毕后,请指向delete[]
它:
delete[] snames;
下一个问题来自这一行:
s1.InputData(sname, snumClasses, snames[i]);
// ^^^^^^^^^
Student
对象有三个数据成员,类的名称,类的数量和类的数组。 InputData
成员函数用于将这些数据成员分配给给定的参数。您已为前两个参数指定了正确的参数(因为string
可以分配给string
而int
可以分配给int
但是您的第三个参数与数据成员的类型不匹配。通过执行snames
发送数组snames[i]
而不是其中的元素是有意义的。
s1.InputData(sname, snumClasses, snames);
// ^^^^^^
这还需要InputData
采用string*
而不是string&
,并且classList
指针只需分配给新的snames
数组:< / p>
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;
}
请注意,考虑到*this
与被复制对象之间的生命依赖关系,需要执行深层复制。
Student::Student(const Student& obj)
{
name = obj.name;
numClasses = obj.numClasses;
std::copy(obj.classList, obj.classList + numClasses, classList);
}
此构造函数的另一个问题是它使用赋值而不是初始化。应使用 member-initializer list
初始化类的数据成员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;
}
此运算符与复制构造函数具有相同的问题,因为它考虑了内存管理。如果我们从*this
分配到rightSide
,我们必须delete[]
当前的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!
}
这看起来恰到好处,但也可以改进。目前,此代码不提供强例外保证。如果new
抛出,*this
的状态将与进入赋值运算符时的状态不同。为了避免这种情况,我们可以应用复制和交换习惯用语,以提供强大的异常保证:
Student& operator=(Student rhs)
{
swap(*this, rhs);
return *this;
}
参数已被更改为按值取值,因此当传入左值时,我们可以复制它并交换其数据成员。函数swap
封装了交换语义。它在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);
}
有关详细信息,请参阅What is the copy-and-swap idiom?
new
:每当您想使用new
时,请考虑标准库中提供的替代方案。它们实现RAII,因此不要求用户自己解除内存。这非常有利,因为它可以减少潜在的内存泄漏并保持代码清洁和高效。我们可以将标准库容器std::vector<T>
用于类列表,并将其替换为string* classList
数据成员:
private:
std::string name;
std::vector<std::string> classList;
请注意我是如何删除numClasses
数据成员的。我们不需要它,因为向量具有size()
成员函数。而且,由于不需要内存管理,我们可以实现Zero规则而不实现default-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;
}
这就是你在main()
中使用它的方法:
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();
}