问题描述
因此,我的目标是创建多个相同类的对象,而无需多次复制一个对象。我想我找到了一种方法,方法是将一个指针数组指向某个对象,但出现分段错误。
不幸的是,在这个项目中,我试图做的是,我没有使用280dp
以外的任何其他库(我必须在这里添加,该类的构造函数将一些参数作为输入)。
有想法吗?
代码如下:
<cstring>
...以及该类的代码:
#include <iostream>
#include <cstring>
using namespace std;
int main(void)
{
int i,floor,classroom; //the floor of the school and the classroom the student is heading to
char *stname;
typedef Student *stptr;
stptr *students = new stptr[15];
for (i = 0; i < 15; i++)
{
cin >> stname;
cin >> floor;
cin >> classroom;
students[i] = new Student(stname,classroom);
}
}
解决方法
我建议不要进行这种指针混淆:
Student** students = new Student*[15];
更容易理解IMO。
您可以使用C ++专有的工具来自动处理对象的字符串数组:
std::string
表示字符串;
std::vector
用于阵列。
对象指针在多态环境中很有用,但即使那样,最好使用smart pointers。
与pointed out by marcinj in the comments一样,name
声明也需要修正。
pointed out by Alan Birtles也在使用stname
未初始化,这可能是段错误的罪魁祸首。
要修复您的代码,请使用当前使用的工具执行类似以下代码的操作,请注意,除修复外,还必须遵守rule of three:
#include <iostream>
#include <cstring>
class Student {
char *name;
int no_floor;
int no_classroom;
public:
Student(const char *nam,int no_fl,int no_cla){//constructor
name = new char[strlen(nam) + 1];
strcpy(name,nam);
no_floor = no_fl;
no_classroom = no_cla;
std::cout << "A new student has been created! with name " << name << " heading to floor: " << no_floor << " class: " << no_classroom << std::endl;
};
~Student(){//destructor
std::cout << "A Student to be destroyed! with name " << name << " is at floor: " << no_floor << " class: " << no_classroom;
delete [] name;
};
Student (const Student& student) { //copy constructor
this->name = nullptr;
*this = student;
}
Student& operator = (const Student& student){ //assignment operator
if(this == &student){
return *this;
}
delete [] this->name;
this->name = new char[strlen(student.name) + 1];
strcpy(this->name,student.name);
this->no_classroom = student.no_classroom;
this->no_floor = student.no_floor;
return *this;
}
};
int main(){
int i,floor,classroom;
char *stname = new char[100];
typedef Student *stptr;
stptr *students = new stptr[15];
for (i = 0; i < 15; i++){
std::cin >> stname;
std::cin >> floor;
std::cin >> classroom;
students[i] = new Student(stname,classroom);
}
delete [] stname;
for (i = 0; i < 15; i++){
delete students[i];
}
//or for the deletion in inverse order
//for (i = 2; i >= 0; i--){
// delete students[i];
//}
delete [] students;
}
,
此代码中无需使用指针,使用对象数组而不是指针,而使用std::string
而不是char*
可以使您的代码更加简单和安全:
#include <iostream>
#include <string>
class Student
{
private:
std::string name;
int no_floor;
int no_classroom;
public:
Student() = default;
Student(const std::string& nam,int no_cla) //constructor
: name(nam),no_floor(no_fl),no_classroom(no_cla)
{
std::cout << "A new student has been created! with name " << name << " heading to floor: " << no_floor << " class: " << no_classroom << std::endl;
};
~Student() //destructor
{
std::cout << "A Student to be destroyed! with name " << name << " is at floor: " << no_floor << " class: " << no_classroom;
};
};
int main(void)
{
int i,classroom; //the floor of the school and the classroom the student is heading to
std::string stname;
Student students[15];
for (i = 0; i < 15; i++)
{
std::cin >> stname;
std::cin >> floor;
std::cin >> classroom;
students[i] = Student(stname,classroom);
}
}
原始代码中存在许多问题。
-
stname
未初始化,因此读入它会导致不确定的行为。您需要使用数组初始化它,例如char *stname = new char[100]
,您必须记住在程序结束时delete
,以避免内存泄漏。还要注意,std::cin >> stname
是不安全的,因为它不知道数组的长度,因此,如果您读取的字符数超过了适合的字符数,将导致不确定的行为。或者,您可以只使用固定长度的数组,这至少解决了必须记住调用delete
:char stname[100]
的问题
- 您永远不会
delete
或students
本身的元素导致内存泄漏。 -
students
返回字符串的长度,该字符串的长度从您实际想要的第二个字符开始,strlen(nam + 1)
返回字符串的长度加上一个空终止符。 - 这还没有给您带来问题,但是您需要实现rule of three并添加一个复制构造函数和赋值运算符
当您不使用原始指针时,所有这四个问题都会消失...
如果您真的不能使用strlen(nam) + 1
,则最好的方法可能是创建自己的字符串类,然后使用该字符串类,以便将所有原始指针crud至少限制在一个位置。例如这样的东西:
std::string
是的,虽然有很多代码,但是如果您的老师对class String
{
public:
String()
:str(nullptr),length(0),capacity(0)
{
}
String(const char* s)
:String()
{
assign(s);
}
String(const String& other)
:String()
{
assign(other.str,other.length);
}
String& operator = (const String& other)
{
assign(other.str,other.length);
return *this;
}
~String()
{
delete[] str;
}
void reserve(size_t new_capacity)
{
// note doens't copy existing data in str to new buffer
delete str;
str = new char[new_capacity + 1];
capacity = new_capacity;
}
friend std::ostream& operator <<(std::ostream& os,const String& str)
{
if (str.length)
{
os.write(str.str,str.length);
}
return os;
}
friend std::istream& operator >>(std::istream& is,String& str)
{
// note can only read up to 100 characters
str.reserve(100);
str.length = 0;
std::istream::sentry sentry(is);
if (sentry)
{
while (str.length < str.capacity)
{
auto ch = is.rdbuf()->sgetc();
if (!std::istream::traits_type::not_eof(ch))
{
is.setstate(std::ios_base::eofbit);
break;
}
if (std::isspace(ch))
{
break;
}
str.str[str.length++] = ch;
is.rdbuf()->sbumpc();
}
}
str.str[str.length] = '\0';
return is;
}
private:
void assign(const char* s)
{
size_t len = strlen(s);
assign(s,len);
}
void assign(const char* s,size_t len)
{
if (len > 0)
{
if (len > capacity)
{
reserve(len);
}
strcpy(str,s);
}
length = len;
}
char* str;
size_t length;
size_t capacity;
};
过敏,这是使代码正确所需要的最低要求。
尽管我同意@anastaciu的评论,但我也建议避免使用原始指针,因为如果您不熟悉C / C ++,可能会遇到很多问题,例如dangling pointers,{{ 3}}和wild pointers。您应该避免使用它们,除非有必要(例如,内存映射的IO或memory leaks,因为您无法想到另一种方法),或者使用内置的容器放置对象(您可以了解关于它们的更多信息unsafe casts)
,好的,我找到了@Alan Birtles答案中出现的双重破坏打印的解决方案。您可以以不同的方式分配内存,而无需复制对象然后销毁它。这是代码:
int i,classroom;
string stname;
Student* students[5];
for(i=0; i<5; i++)
{
cin >> stname;
cin >> floor;
cin >> classroom;
students[i] = new Student(stname,classroom);
}
for(i=0; i<5; i++)
{
delete students[i];
}
..然后就可以了!