包含指针的用户定义对象存储在数组中时中断

User-defined object containing pointer breaks when stored in array

当我在 A 中存储普通 int 并执行简单的获取函数时:

#include <iostream>
class A
{
    int p;
public:
    void setint(int p_x);
    int getint();
};

void A::setint(int p_x) {p = p_x;} // set p (type int)

int A::getint() {return p;} // get p (type int)

int main()
{
    A arr_a[5];
    arr_a[0].getint();
}

它编译并退出,代码为 0。但是,当我将 int 更改为 int* 并尝试执行相同操作时:

#include <iostream>
class A
{
    int* p;
public:
    void setint(int p_x);
    int getint();
};

void A::setint(int p_x) {*p = p_x;} // set int pointed to by p (type int)

int A::getint() {return *p;} // get int pointed to by p (type int)

int main()
{
    A arr_a[5];
    arr_a[0].getint();
}

编译正常,但退出时代码为 3221225477。为什么会这样,还有什么方法可以将指针存储在 A 中并将 A 存储在数组中?

您的程序有一个严重缺陷。您给定的两个代码片段(问题中的案例 1 和案例 2)都具有 未定义的行为。来看看怎么样

案例一:代码片段 1

在您的代码片段 1 中,由于数据成员 p 是内置类型并且您尚未对其进行初始化,因此 p 具有 垃圾值 并使用(访问)这个值会导致 未定义的行为 这正是你的情况。

当你写道:

 A arr_a[5];//this creates a 1D array of size 5 having elements of type `A` but the elements are default initialized 
 arr_a[0].getint();//this result in undefined behavior

上述语句创建了一个大小为 5 的一维数组,其元素类型为 A问题是因为你没有初始化这个数组,它的元素是默认初始化这意味着数据成员的值p也是默认初始化的。但是由于您没有对变量 p 使用 in-class 初始值设定项 p 具有垃圾值,这会导致 未定义的行为.

您可以通过查看输出来确认这一点 here

案例一的解决方案

你可以通过使用 in-class initializer 初始化数据成员 p 来解决这个问题,如下所示:

#include <iostream>
class A
{
    int p = 0;//USE IN-CLASS INITIALIZER
public:
    void setint(int p_x);
    int getint();
};

void A::setint(int p_x) {p = p_x;} // set p (type int)

int A::getint() {return p;} // get p (type int)

int main()
{
    A arr_a[5];
    std::cout<<arr_a[0].getint();//now ok because we have initilaized p
}

案例二:代码片段 2

在这种情况下,唯一的区别是现在数据成员 p 是指向 int 的指针,即 int*。与上一种情况类似,指针变量有一个垃圾值,如果您尝试像在主函数中那样使用它,可能会导致未定义的行为通过写作:

A arr_a[5];//create a 1D array of objects `A` but the elements(A objects) are default initialized  
arr_a[0].getint();//this result in undefined behavior

案例二的解决方案

你可以通过使用 in-class initializer 初始化数据成员 p 来解决这个问题,如下所示:

#include <iostream>
class A
{
    int* p = nullptr;//USE IN-CLASS INITIALIZER
public:
    void setint(int p_x);
    int getint();
    //other members like constructor and destructor to allocate and deallocate memory
    //so that getint and setint doesn't dereference nullptr
};

void A::setint(int p_x) 
{    if(p!=nullptr)// add a check here to see p isn't null
     {
        *p = p_x;
         
     }
     else 
    {
        std::cout<<"WARNING: Dereferencing a nullptr can lead to UB";
    }

} 

int A::getint() // add a check here to see p isn't null
{   if(p!= nullptr)
    {
    std::cout<<"yes"<<std::endl;   
    return *p;
      
    }
    else 
    {
        std::cout<<"WARNING: Dereferencing a nullptr can lead to UB";
        return -1;
    }
} 

int main()
{
    A arr_a[5];
    arr_a[0].getint();//now ok(assuming `p` isn't nullptr) because we have initilaized p 
}

总结

您提供的两个代码片段都有未定义的行为。您可以通过使用 in-class initlaizers 将数据成员 p 初始化为默认值来解决这两个问题。

在你的第二种情况下 A arr_a[5] 只需创建一个包含 5 个 A 的数组。但是 对于每个 A,指针是一个未定义的数字(可能是 0x0),因此 *p 是一个未定义的行为。您应该添加 A::A()A::~A() 来管理 class 中的指针 就像这样:

#include <iostream>
class A
{
    int *p;

public:
    A();
    ~A();
    void setint(int p_x);
    int getint();
};

A::A() : p(new int) {}
A::~A() { delete p; }

void A::setint(int p_x) { *p = p_x; } // set int pointed to by p (type int)

int A::getint() { return *p; } // get int pointed to by p (type int)

int main()
{
    A arr_a[5];
    arr_a[0].getint();
}