我对C ++很陌生,我需要创建MyString类,以及从另一个子字符串创建新MyString对象的方法,但是在创建类时以及使用我的方法打印它时选择子字符串更改。
这是我的代码:
#include <iostream>
#include <cstring>
using namespace std;
class MyString {
public:
char* str;
MyString(char* str2create){
str = str2create;
}
MyString Substr(int index2start, int length) {
char substr[length];
int i = 0;
while(i < length) {
substr[i] = str[index2start + i];
i++;
}
cout<<substr<<endl; // prints normal string
return MyString(substr);
}
void Print() {
cout<<str<<endl;
}
};
int main() {
char str[] = {"hi, I'm a string"};
MyString myStr = MyString(str);
myStr.Print();
MyString myStr1 = myStr.Substr(10, 7);
cout<<myStr1.str<<endl;
cout<<"here is the substring I've done:"<<endl;
myStr1.Print();
return 0;
}
这是输出:
嗨,我是一个字符串
的字符串
STRI
这是我完成的子字符串:
в™|
答案 0 :(得分:0)
您的函数Substr
通过在返回值substr
对象中存储指向它的指针来间接返回本地变量MyString
的地址。一旦局部变量超出范围,取消引用指向局部变量的指针就无效了。
我建议您决定您的类是否包装外部字符串,或者拥有自己的字符串数据,在这种情况下,您需要将输入字符串复制到成员缓冲区。
答案 1 :(得分:0)
必须通过这个来解释出错的地方,所以请耐心等待。
int main() {
char str[] = {"hi, I'm a string"};
分配一个包含17个字符的临时数组(16个字母加上一个终止空值),在其中放置字符“hi,I is a string”,并以null结束。临时意味着它听起来像。当函数结束时,str
消失了。指向str
的任何东西现在指向垃圾。它可能会晃动一段时间并在重复使用和覆盖之前给出一些相似的生命,但实际上它只是一个僵尸,只能被信任杀死你的程序并吃掉它的大脑。
MyString myStr = MyString(str);
创建另一个临时变量myStr。使用字符数组调用构造函数。那么让我们来看看构造函数:
MyString(char* str2create){
str = str2create;
}
指向一个字符,在这种情况下,它将有一个指向main str
的第一个元素的指针。该指针将分配给MyString的str
。没有复制“嗨,我是一个字符串”。主电源str
和MyString的str
指向内存中的相同位置。这是一种危险的情况,因为改变一方会影响另一方。如果一个str
消失,另一个消失。如果覆盖了一个str
,那么另一个MyString(char* str2create){
size_t len = strlen(str2create); //
str = new char[len+1]; // create appropriately sized buffer to hold string
// +1 to hold the null
strcpy(str, str2create); // copy source string to MyString
}
也会被覆盖。
构造函数应该做的是:
str
一些警告:这真的很容易打破。例如,传入一个永不结束的str2create,并且strlen将旋转到未分配的内存中,结果将是不可预测的。
现在我们假设没有人特别恶意,只会输入好的价值,但这在现实世界中被证明是非常糟糕的假设。
这也强制要求析构函数释放virtual ~MyString(){
delete[] str;
}
str
它还增加了复制和移动构造函数以及复制和移动赋值运算符的要求,以避免违反Rule of Three/Five。
回到OP的代码......
myStr
和myStr
指向内存中的相同位置,但这还不错。因为这个程序是一个微不足道的程序,它永远不会成为一个问题。 str
和myStr.Print();
都会在同一时间到期,也不会再次修改。
str
将正确打印,因为myStr
或 MyString myStr1 = myStr.Substr(10, 7);
中没有任何更改。
MyString Substr(int index2start, int length) {
char substr[length];
要求我们查看MyString :: Substr以了解会发生什么。
substr
创建大小为length的临时字符数组。首先,这是非标准的C ++。它不会在很多编译器下编译,只是不要在第一时间这样做。其次,这是暂时的。当函数结束时,该值为垃圾。不要指向 int i = 0;
while(i < length) {
substr[i] = str[index2start + i];
i++;
}
的任何指针,因为它不会长时间使用它们。第三,没有为终止空值保留空间。这个字符串将是一个等待发生的缓冲区溢出。
cout<<substr<<endl; // prints normal string
好的,这很不错。从源复制到目标。缺少的是null终止,因此char数组的用户知道它何时结束。
return MyString(substr);
那缓冲区溢出等待发生?刚发生。 Whups。你运气不好,因为看起来它有效而不是崩溃,让你知道它没有。在正确的地方,一定是内存中的空值。
substr
这创建了一个指向substr
的新MyString。就在}
命中函数结束之前就死了。这个新的MyString几乎立即指向垃圾。
MyString Substr(int index2start, int length)
{
std::unique_ptr<char[]> substr(new char[length + 1]);
// unique_ptr is probably paranoid overkill, but if something does go
// wrong, the array's destruction is virtually guaranteed
int i = 0;
while (i < length)
{
substr[i] = str[index2start + i];
i++;
}
substr[length] = '\0';// null terminate
cout<<substr.get()<<endl; // get() gets the array out of the unique_ptr
return MyString(substr.get()); // google "copy elision" for more information
// on this line.
}
Substr应该做什么:
substr
回到OP的代码中,返回到主函数cout<<myStr1.str<<endl;
开始被重用和覆盖。
myStr1.str
打印cout<<"here is the substring I've done:"<<endl;
myStr1.Print();
,我们已经可以看到其中一些已被重复使用和销毁。
d1 <- read.table(text="visibility,cloudCover,pressure
10,0.03,1014.06", header=TRUE, stringsAsFactors=FALSE, sep=",")
d2 <- read.table(text="windSpeed,windBearing
16.27,280", header=TRUE, stringsAsFactors=FALSE, sep=",")
d3 <- read.table(text="visibility,pressure
2.97,1010.04", header=TRUE, stringsAsFactors=FALSE, sep=",")
dplyr::rbind_list(d1, d2, d3)
## Source: local data frame [3 x 5]
##
## visibility cloudCover pressure windSpeed windBearing
## (dbl) (dbl) (dbl) (dbl) (int)
## 1 10.00 0.03 1014.06 NA NA
## 2 NA NA NA 16.27 280
## 3 2.97 NA 1010.04 NA NA
更多的死亡,更多的破坏,更少的绳索。
未来不做的事情:
分享应该复制数据的指针。
指向临时数据的指针。
非空终止字符串。