Ошибка сегментации при назначении указателя - PullRequest
0 голосов
/ 22 октября 2019

Когда я пытаюсь инициировать класс, который имеет переменные указателя класса, к другим объектам класса, я получаю сегментацию с ошибкой сегментации. Пример кода ниже.

Я попытался инициализировать переменные с помощью ключевого слова 'new', чтобы сначала очистить классы, а также вручную, используя malloc перед назначением, и попытался просто назначить переменные без какого-либо необычного распределения и ничегоработает.

// Caller
void Application::run()
{
    Network network(this);
    network.start();
}

// Seg fault here, at the line that assigns window
Network::Network(Application *app)
{
    application = new Application();
    window = new sf::RenderWindow();
        application = app;
    window = app->window;
}

// Header for Network
class Network 
{
    public:
        Application* application;
        sf::RenderWindow* window;
    // Functions
    Network(Application *app);
        void start();
        void networkLoop();
    private:
    protected:
};

// Header for Application
class Application 
{
    public:
    sf::RenderWindow* window;
    Theme* theme;
    // Functions
    Application();
    void run();
    private:
    protected:
};

// Main
#include "application.h"
#include <X11/Xlib.h>

int main()
{
    XInitThreads();
    Application* app;
    app->run();
}

Я довольно новичок в использовании C ++, так что прости меня за очевидные ошибки памяти. Я пытался найти решение, но ничего, что я нашел, не сработало. Я принимаю указатель на Application в качестве входных данных для конструктора, потому что я хочу, чтобы один и тот же экземпляр RenderWindow и Application содержался в других объектах.

EDIT: я чувствую себя действительно глупо, но я изменил main(), чтобы создать Application с Application app вместо Application* app и, похоже, это исправило. Но все же: кто-то может объяснить, почему это работает, а использование указателя - нет?

Ответы [ 2 ]

3 голосов
/ 22 октября 2019

Чтобы ответить на ваши изменения, разница между

Application* app;

и

Application app;

состоит в том, что распределяется и строится, а что нет.

ВВ первом примере компилятор выделяет достаточно места в стеке для хранения указателя, но не выделяет места для того, на что он может указывать - вам нужно сделать это вручную с помощью new. Вот почему ваша программа не работала - у вас было место для указателя, но когда вы отменили ссылку на этот указатель с помощью ->, Application не ожидал на другом конце. Вы сказали ему найти Application, взять свойство window и сохранить его где-нибудь еще, но это свойство window никогда не создавалось.

Во втором примере компилятор выделяет достаточно местадля полного Application объекта и вызывает конструктор для вас. Поэтому, когда вы отмените ссылку на указатель на этот раз, там будет ожидаться Application.


Я хочу добавить еще один фрагмент информации. В первом примере указатели не являются объектами и не имеют конструкторов для вызова, поэтому, если вы не присваиваете им значение, их начальные значения являются практически случайными - какими бы они ни были в стеке в то время. Таким образом, ваш указатель не только не указывал на Application, но, вероятно, даже не указывал на используемую область памяти, поэтому вы получили ошибку сегментации.

3 голосов
/ 22 октября 2019

Проблема в вашем main()

int main()
{
    XInitThreads();
    Application* app;
    app->run();
}

Вы должны использовать new здесь и выделить ваше Application:

int main()
{
    XInitThreads();
    Application* app = new Application(...);
    app->run();
}

Однако, Дон't сделать это в Network. Это создаст утечку памяти. Сделайте это вместо этого:

Network::Network(Application *app)
{
    application = app;
    window = app->window;
}

Редактировать:

Может кто-нибудь объяснить, почему это работает, а использование указателя - нет?

Итак, указатели не выделяют память самостоятельно. Они просто указатели. Они держат адреса. Они не выделяют память самостоятельно, чтобы разрешить такие вещи:

int a =0;
int *b = &a; // holds address of a

Если указатели выделяют данные здесь, это может вызвать утечку памяти или другие вещи. Таким образом, если вы хотите хранить новый объект независимо от других переменных, вам нужно использовать new или какой-нибудь умный указатель:

int* c = new int; // c holds an int independent of a or b

Таким образом, если вы просто делаете что-то вроде этого:

int* d;

d не имеет выделенной памяти на своей позиции. Это заполнитель, который может содержать любой адрес. Он может содержать существующий адрес или новый адрес. Но ему нужно сохранить некоторый действительный адрес, прежде чем вы сможете его использовать.

Если вы хотите создать экземпляр Application, вам нужно либо выделить его с помощью new, либо, как отмечали другие,даже не используйте указатель вообще, что на самом деле является рекомендуемым курсом действий:

int main()
{
    XInitThreads();
    Application app;
    app.run();
}
...