#include<iostream>
using namespace std;
class MyString{
private:
char *str=new char[10];
public:
MyString(){*str='\0';} //default constructor
MyString(char *s){ //parameterized constructor
str=s;
}
private:
int length(char* s){
int i=0;
while(s[i]!='\0')
i++;
return i;
}
char* delchar(char* s,int count,int start){
int i,j=0;
char *temp= new char[10];
for(i=start;i<start+count;i++){
s[i]=' ';
}
for(i=0;i<length(s);i++){
if(s[i]!=' ')
temp[j++]=s[i];
}
s=temp;
return s;
}
public:
MyString operator-(MyString s){
int i=0,j=0,count=0,start=-1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str=str;
while(temp.str[i]!='\0'){
j=0;
start++;
while(s.str[j]!='\0'){
if(temp.str[i]==s.str[j]){
count++;
i++;
j++;
if(count==length(s.str)){//checks if the count
temp.str=delchar(temp.str,count,start);
i=i-count;
start=i-1;
count=0;
}
}
else{
i++;
count=0;
break;
}
}
}
return temp;
}
~MyString(){
delete str;
}
friend ostream &operator<<(ostream &stream,MyString& s){
stream<<s.str<<endl;
return stream;
}
};
int main(){
char *p= new char[20];
char *q= new char[10];
cin>>p;
cin>>q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3=s1-s2;
cout<<s3;
delete p;
delete q;
return 0;
}
上面的代码重载了 - 运算符。它试图从主字符串中减去子字符串,例如input1:joshmathews input2:josh output:mathews。我试图将输出存储在一个新的MyString对象s3中。当我使用如上所示的析构函数时,输出s3 返回null。但是,当我不使用析构函数时,我得到了预期的输出。任何人都可以帮忙吗?
答案 0 :(得分:0)
主要问题是Operater-返回一个由默认复制构造函数复制的本地对象 - 默认复制构造函数将s3指向temp MyString的完全相同的内存/缓冲区,当该temp被破坏时,它消除s3正在使用的内存。
这被称为悬空指针。您可以在此处阅读更多内容:http://en.wikipedia.org/wiki/Dangling_pointer#Cause_of_wild_pointers
下面的代码是我为使程序执行并返回有效结果所做的更改,同时完成且没有错误。但要清楚,这个更改版本,运行时错误或没有运行时错误存在问题,但这将有助于说明一些重点。
当你定义一个分配内存的类型时,你真的开始做一些不自由的事情。我在下面的修改后的版本完全摆脱了析构函数,所以实际上它会泄漏内存,直到程序结束。虽然析构函数无效,但删除它会使程序完成。但这将是你不应该接受的一般事情之一。
我添加了一个复制构造函数和一个复制赋值运算符:
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
注意这两个中的strcpy_s。正在做的是从参数中复制字符串,而不是仅仅尝试指向与参数完全相同的地址处的完全相同的字符串。如果参数在您的版本中被破坏,它会消除一些内存,因此我们可以接受默认的复制构造函数等,因为默认情况下它们是指向相同内容的浅拷贝。这是分配内存的负担之一 - 你需要在你的析构函数〜和〜你的一些构造函数中处理它。这被称为“三个规则”:
如果您需要自己显式声明析构函数,复制构造函数或复制赋值运算符,您可能需要明确声明它们中的所有三个。
以下是关于三条规则的维基百科链接:http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
你需要一个析构函数,这就是你需要其他人的线索。原因就像我说的那样 - 默认情况下你会免费获得一个复制构造函数,但如果有资源点,它只会使所有内容指向相同的内容,但是你不能在不影响其他人的情况下删除它。 / p>
另外,在delchar中我将这一行添加到底部:
temp[j] = '\0';
字符串必须始终以null结尾,并且您只将实际字母字符复制到temp中,因此如果没有空字符,strcpy就不知道从哪里结束。
如果你还记得我把你的析构函数拉出来,那就是我所做的改变。
析构函数也有一个问题,如果你使用new来创建一个数组,比如
char* c = new char[10];
然后你需要以一种新的方式删除它作为一个数组,如下所示:
delete [] c;
接下来你要研究的是你的MyString结构是如何发生的。如果你单步执行,你会看到str成员变为new'd - 这意味着它将一个有效的地址保存到你可以使用的缓冲区中 - 但是如果使用了MyString(char * s)构造函数,那么它就是字面意思只取这个s的新地址,并使str保持该地址 - 这意味着指向有效缓冲区的旧地址丢失,并且新内存的缓冲区不能被释放。这是一个泄漏。您可以使用我添加的构造函数中的strcpy_s策略将MyString(char * s)中s指向的内容复制到new'd缓冲区中。这将在代码中产生巨大的积极影响。
并且不要为那里的所有精英主义者打扰 - 他们中的许多人天生就做倒立,所以他们无法看到与新手做出诚实努力的关系。
以下是完整的修改代码:
#include<iostream>
using namespace std;
class MyString{
private:
char *str = new char[10];
public:
MyString(){ *str = '\0'; } //default constructor
MyString(char *s){ //parameterized constructor
str = s;
}
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
private:
int length(char* s){
int i = 0;
while (s[i] != '\0')
i++;
return i;
}
char* delchar(char* s, int count, int start){
int i, j = 0;
char *temp = new char[10];
for (i = start; i<start + count; i++){
s[i] = ' ';
}
for (i = 0; i<length(s); i++){
if (s[i] != ' ')
temp[j++] = s[i];
}
temp[j] = '\0';
s = temp;
return s;
}
public:
MyString operator-(MyString s){
int i = 0, j = 0, count = 0, start = -1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str = str;
while (temp.str[i] != '\0'){
j = 0;
start++;
while (s.str[j] != '\0'){
if (temp.str[i] == s.str[j]){
count++;
i++;
j++;
if (count == length(s.str)){//checks if the count
temp.str = delchar(temp.str, count, start);
i = i - count;
start = i - 1;
count = 0;
}
}
else{
i++;
count = 0;
break;
}
}
}
return temp;
}
~MyString(){
//delete str;
}
friend ostream &operator<<(ostream &stream, MyString& s){
stream << s.str << endl;
return stream;
}
};
int main(){
char *p = new char[20];
char *q = new char[10];
cin >> p;
cin >> q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3 = s1 - s2;
cout << s3;
delete p;
delete q;
return 0;
}