上周,我们使用类和向量创建了一个管理字符串集的程序。我能够完成这100%。本周,我们必须使用简单的单链表替换我们用于在类中存储字符串的向量。
该函数基本上允许用户声明空字符串集,并且只设置一个元素。在主文件中,有一个向量,其元素是包含setName和strSet(class)的结构。
这是我的问题:它处理类的复制构造函数。当我删除/注释掉复制构造函数时,我可以根据需要声明任意数量的空集或单集,并输出它们的值而没有问题。但是我知道当我实现程序的其余部分时,我显然需要复制构造函数。当我离开复制构造函数时,我可以声明一个集合,单个或空,并输出其值。但是如果我声明第二组,并且我尝试输出前两组中的任何一组,我会得到一个分段错误。此外,如果我尝试声明超过2组,我会得到一个分段错误。任何帮助将不胜感激!!
以下是我对所有内容的基本实现的代码:
这是setcalc.cpp :(主文件)
#include <iostream>
#include <cctype>
#include <cstring>
#include <string>
#include "strset2.h"
using namespace std;
// Declares of structure to hold all the sets defined
struct setsOfStr {
string nameOfSet;
strSet stringSet;
};
// Checks if the set name inputted is unique
bool isSetNameUnique( vector<setsOfStr> strSetArr, string setName) {
for(unsigned int i = 0; i < strSetArr.size(); i++) {
if( strSetArr[i].nameOfSet == setName ) {
return false;
}
}
return true;
}
int main() {
char commandChoice;
// Declares a vector with our declared structure as the type
vector<setsOfStr> strSetVec;
string setName;
string singleEle;
// Sets a loop that will constantly ask for a command until 'q' is typed
while (1) {
cin >> commandChoice;
// declaring a set to be empty
if(commandChoice == 'd') {
cin >> setName;
// Check that the set name inputted is unique
if (isSetNameUnique(strSetVec, setName) == true) {
strSet emptyStrSet;
setsOfStr set1;
set1.nameOfSet = setName;
set1.stringSet = emptyStrSet;
strSetVec.push_back(set1);
}
else {
cerr << "ERROR: Re-declaration of set '" << setName << "'\n";
}
}
// declaring a set to be a singleton
else if(commandChoice == 's') {
cin >> setName;
cin >> singleEle;
// Check that the set name inputted is unique
if (isSetNameUnique(strSetVec, setName) == true) {
strSet singleStrSet(singleEle);
setsOfStr set2;
set2.nameOfSet = setName;
set2.stringSet = singleStrSet;
strSetVec.push_back(set2);
}
else {
cerr << "ERROR: Re-declaration of set '" << setName << "'\n";
}
}
// using the output function
else if(commandChoice == 'o') {
cin >> setName;
if(isSetNameUnique(strSetVec, setName) == false) {
// loop through until the set name is matched and call output on its strSet
for(unsigned int k = 0; k < strSetVec.size(); k++) {
if( strSetVec[k].nameOfSet == setName ) {
(strSetVec[k].stringSet).output();
}
}
}
else {
cerr << "ERROR: No such set '" << setName << "'\n";
}
}
// quitting
else if(commandChoice == 'q') {
break;
}
else {
cerr << "ERROR: Ignoring bad command: '" << commandChoice << "'\n";
}
}
return 0;
}
这是strSet2.h:
#ifndef _STRSET_
#define _STRSET_
#include <iostream>
#include <vector>
#include <string>
struct node {
std::string s1;
node * next;
};
class strSet {
private:
node * first;
public:
strSet (); // Create empty set
strSet (std::string s); // Create singleton set
strSet (const strSet ©); // Copy constructor
// will implement destructor and overloaded assignment operator later
void output() const;
}; // End of strSet class
#endif // _STRSET_
这里是strSet2.cpp(类的实现)
#include <iostream>
#include <vector>
#include <string>
#include "strset2.h"
using namespace std;
strSet::strSet() {
first = NULL;
}
strSet::strSet(string s) {
node *temp;
temp = new node;
temp->s1 = s;
temp->next = NULL;
first = temp;
}
strSet::strSet(const strSet& copy) {
cout << "copy-cst\n";
node *n = copy.first;
node *prev = NULL;
while (n) {
node *newNode = new node;
newNode->s1 = n->s1;
newNode->next = NULL;
if (prev) {
prev->next = newNode;
}
else {
first = newNode;
}
prev = newNode;
n = n->next;
}
}
void strSet::output() const {
if(first == NULL) {
cout << "Empty set\n";
}
else {
node *temp;
temp = first;
while(1) {
cout << temp->s1 << endl;
if(temp->next == NULL) break;
temp = temp->next;
}
}
}
答案 0 :(得分:1)
C ++标准规定标准容器中使用的类型(例如std :: vector)必须是可复制构造且可分配的。
由于您尚未在类strSet上实现自定义赋值运算符,因此编译器将为您生成一个执行简单成员复制的运算符。在您的情况下,这意味着将直接复制“第一个”指针。显然这意味着现在两个对象“拥有”集合中的节点,并且当它被释放两次时会发生崩溃。
一些提示:
实现一个与复制构造函数
阅读通过引用传递对象,并尽可能通过const引用。传递值时,您正在对容器和字符串进行大量不必要的复制。
e.g。
bool isSetNameUnique(const vector&amp; strSetArr,const string&amp; setName)
祝你好运:)答案 1 :(得分:0)
当strSet
复制构造函数的参数为空时,它不会分配成员first
。这会导致未定义的行为。
此外,编辑前显示的strSet
赋值运算符(operator=
)肯定是错误的;确定复制构造函数但允许编译器隐式定义析构函数和赋值运算符并不是一个好主意。请参阅Rule of Three。
当他们需要进行大量管理(如本案例)时,实施三巨头的一种常见方式如下:
class strSet {
private:
void cleanup();
void create_from(const node* n);
// ...
};
strSet::~strSet() { cleanup(); }
strSet::strSet(const strSet& copy) : first(NULL) { create_from(copy.first); }
strSet& strSet::operator=(const strSet& rtSide) {
if (this != &rtSide) {
cleanup(); // trash old contents of *this
create_from(rtSide.first); // clone contents of rtSide
}
return *this;
}
答案 2 :(得分:0)
这看起来有点奇怪:
strSet::strSet(string s) {
node *temp;
temp = new node;
temp->s1 = s;
temp->next = NULL;
first = temp;
}
如果'第一'指向已经存在的东西怎么办?然后,您将有效地杀死上一个列表并导致内存泄漏。