【问题标题】:C++ classes, segmentation fault when calling member functionC ++类,调用成员函数时出现分段错误
【发布时间】:2013-05-02 20:21:47
【问题描述】:

我现在正在学习 C++,并且一直在尝试各种类,只是为了弄清楚它们是如何工作的。我以前只用 Java 编写过类。

在我的代码中,我有一个类定义和一个要测试的驱动程序。在 cmets 中,我提到了哪些有效,哪些无效。我真的很想知道为什么以一种方式实例化对象但以其他方式实例化对象会出错。是编译器、make 文件还是类代码?构造函数/默认构造函数?当我像教科书一样将我的代码与其他代码进行比较时,我看不出我哪里出错了。

在 Linux Mint 13 上使用:code::blocks 10.5。

头文件:

#ifndef ENEMY_H
#define ENEMY_H
#include <string>
using namespace std;

class Enemy
{
public:
    Enemy();
    Enemy(int, int, string); 
    ~Enemy();

    string display();

    // setter and getters:
    int getHP();
    void setHP(int);
    int getX();
    void setX(int);
    int getY();
    void setY(int);
    string getName();
    void setName(string);

private:
    int hitpoints;
    int x_pos;
    int y_pos;
    string name;
};
#endif // ENEMY_H

成员函数定义:

#include "Enemy.h"
#include <iostream>
#include <string>


// default ctor 
Enemy::Enemy()
{
    cout << "Creating enemy with default ctor.\n";
}

//ctor
Enemy::Enemy(int x, int y, string str)
{
    cout << "Creating object with  name: " << str << endl;
    setX(x);
    setY(y);
    setHP(100);
    setName(str);
}

//dtor
Enemy::~Enemy()
{
    cout << "destroying Enemy: " << name << endl;
}

string Enemy::display()
{
    cout<<name<<" -  x: "<<x_pos<<", y: "<<y_pos<<", HP: "<<hitpoints<<endl;
}

int Enemy::getHP(){
    return hitpoints;
}
void Enemy::setHP(int hp){
   hitpoints = hp;
}
int Enemy::getX(){
    return x_pos;
}
void Enemy::setX(int x){
    x_pos = x;
}
int Enemy::getY(){
    return y_pos;
}
void Enemy::setY(int y){
    y_pos = y;
}
string Enemy::getName(){
    return name;
}
void Enemy::setName(string objectName){
    name = objectName;
}
// end of function definitions

司机:

#include "Enemy.h"
#include <iostream>
#include <string>

using namespace std;

int main()
{
    cout << "Program started.\n" << endl;

    // initialise a few Enemy objects
    Enemy E1(1, 1, "E1");
    Enemy E2(2, -4, "E2");
    Enemy E3;
    Enemy E4;
    Enemy *E5 = new Enemy(4, 5, "E5");


    E1.display(); // <- success!
    E2.display(); // <- success!
    E3.display(); // <- segmentation fault at run time
    E4.display(); // <- segmentation fault at run time
    E5.display(); // <- compile time error "request for member
                  //    'display'" in 'E5'. which is of
                  //    non-class type 'enemy'

    cout << "end of program.\n";
    return 0;
}

【问题讨论】:

  • 欢迎来到 Stack Overflow。请花时间阅读faq;你会得到一个徽章(c:
  • @PeterWood 是不是只有超级信任才能看到的特殊隐形徽章?
  • @StarPilot 如果您访问faq 的每个部分,就会获得Analytical 徽章。

标签: c++ class segmentation-fault


【解决方案1】:

导致段错误的原因是您正在离开应该返回 string 的函数的边缘(这是未定义的行为):

string Enemy::display()
//^^^^ you're supposed to return a string
{
    cout<<name<<" -  x: "<<x_pos<<", y: "<<y_pos<<", HP: "<<hitpoints<<endl;
    // no return statement
}

其余的都在@chris 的回答中。

【讨论】:

  • 哦,嘿,我没注意到。我不介意您添加对其他人的解释。用不同的方式解释它总是好的。 @OP,打开你的编译器警告,因为它肯定会告诉你这个。
  • @chris 不用,你解释的很好。
【解决方案2】:

当你说Enemy E3; 时,你调用了默认构造函数。它负责默认初始化您的数据成员。虽然这意味着您的 std::string 成员被初始化为空字符串,但这也意味着您的其他成员未初始化(就像 int i; std::cout &lt;&lt; i; 的做法一样)。当您读取输出语句中的值时,这会导致未定义的行为,这意味着任何事情都可能发生。它选择了崩溃。

至于E5,它是一个指针。您需要取消引用它以获取可以调用成员的对象:

(*E5).display();

还有一个捷径可以做到这一点:

E5->display();

正如 jrok 所指出的,您也不会从 display 返回任何内容,即使它应该返回 std::string。这是除 main 之外的每个函数的未定义行为,在到达右大括号时将返回 0。

附带说明,您有内存泄漏,因为您没有delete E5;。在这种情况下,几乎可以肯定操作系统会负责释放该内存,但是将其放入一个循环中,您会看到您的内存不断增加。

如果您需要动态分配单个对象,请使用智能指针:

std::unique_ptr<Enemy> E5(new Enemy(4, 5, "E5"));

在 C++14 中,我们还获得了 std::make_unique,谢天谢地,我们不再需要 new

【讨论】:

  • 但是如果这是在调试中编译的,原语不应该被赋值为0吗?为什么E3会导致段错误?不批评您的回答,我同意您所说的一切,但不确定 E3,除非它正在发布中编译。
  • @Ryan,不一定,尽管我知道,例如,MSVC 会以明显的模式填充未初始化的内存。编译器会警告使用未初始化的变量。也可能是缺少 return 语句的未定义行为。
  • 呃!我应该在我的默认构造函数中初始化数据成员。出于某种原因,我认为默认构造函数会自动将它们初始化为 0、0、NULL。但这只有在我没有创建自己的默认 ctor 并且编译器提供了一个的情况下,对吧?感谢您关于 E5 的说明。我也应该知道使用“->”而不是“。”为指针。我会尽量记住在使用“新”后删除。谢谢大家。
  • @MatthewZeitgeistBishop,编译器生成的默认构造函数也不会初始化它们。如果未初始化,则数据成员默认初始化。对于像 int 这样的内置类型,这意味着它未初始化,这也扩展到某些 POD 类。对于std::string、你的类等,调用默认构造函数。
【解决方案3】:

关于显示的 E3 和 E4 段错误,您正在尝试打印尚未初始化的变量。而且您的 E5 是一个指针,您可能应该取消引用它以获得对该方法的访问权限。

【讨论】:

    【解决方案4】:

    首先,不要在头文件中放置 using 命名空间声明。在那里使用 std::string 并将命名空间声明放在你的 .cpp 中。

    其次,在默认构造函数中为整数提供默认值(0 是合理的。字符串会自动初始化为空字符串,因此除非需要,否则您不必对它们做任何事情。

    Enemy::Enemy()
    {
      x_pos = 0;
      y_pos = 0;
      ...;
    }
    

    最后,E5 是指向 Enemy(内存中的一个位置)的指针,这意味着您必须使用箭头运算符 (->) 而不是 .访问它的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      相关资源
      最近更新 更多