我已经调试这段代码好几个小时了,试图让输出正确无误。它在早期工作,但输出中存在逻辑错误,因此我进入并在输出函数中添加了循环和一个额外参数。
现在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;
}
太长别读--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