Разыменование указателя на член класса контейнера - PullRequest
1 голос
/ 28 апреля 2020

У меня есть следующая проблема, которую я хочу решить. Я начал с большого класса P, который я хотел разделить. Поэтому часть функциональности была перенесена в новый класс Q. Однако я, похоже, не могу заставить этих двоих общаться должным образом. Чтобы сделать это немного нагляднее, я сделал этот игрушечный пример:

#include <iostream>

class Q {
public:
    int* x_ptr;

    Q(){ } // Is there any way to not have to write default constructors?

    Q(int* x_ptr){
        x_ptr = x_ptr;
    }

    void say_x(){
        std::cout << *x_ptr << std::endl;
    }

    void change_x(){
        *x_ptr += 1;
    }
};

class P {
public:
    Q q;
    int x;

    P(int x){
        x = x;
        q = Q(&x);
    }
};

int main(){
    P my_p = P(10);
    my_p.q.say_x();
    my_p.q.change_x();
    std::cout << my_p.x << std::endl;
}

Я хочу, чтобы класс Q отвечал за изменение Px. Для этого я подумал, что передаю ссылку на Px в Q, когда это создано будет работать. Однако это приводит к ошибке сегментации. Есть ли способ сделать Q способным получать доступ и изменять x? Причина этого в том, что я в конечном итоге хочу иметь ряд классов Q1, Q2 и c, которые все отвечают за различные операции на Px, где Px будет более сложным типом данных, чем простой int.

1 Ответ

2 голосов
/ 28 апреля 2020

Ваши проблемы возникают из-за того, что вы используете одинаковые имена переменных как для аргументов функции, так и для членов класса. Хотя это разрешено, это (ИМХО), очень плохая практика. Если вы хотите сохранить имена, вам нужно будет добавить явный this-> в функции, где вам нужно различать guish между двумя, в противном случае ваши имена аргументов будут 'shadow' члены класса.

Итак, ваш Q конструктор, сохраняющий конфликт имен, должен быть:

    Q(int* x_ptr) {
        this->x_ptr = x_ptr;
    }

, а ваш P конструктор будет:

    P(int x) {
        this->x = x;
        q = Q(&(this->x));
    }

Однако, с простыми изменениями имени в аргументах, это намного яснее:

    Q(int* arg_x_ptr) {
        x_ptr = arg_x_ptr;
    }
//...
    P(int arg_x) {
        x = arg_x;
        q = Q(&x);
    }

В вашем коде строка q = Q(&x); в P выглядит так: Конструктор передает адрес временного объекта, заданный в качестве аргумента, который вызывает ошибку памяти (ошибка сегментации), когда вы позже попытаетесь изменить его.

Примечание. На ваш комментарий не нужно указывать конструктор по умолчанию для Q - вы можете удалить это при условии, что вы дадите параметр 'required' при объявлении / установке элемента q в P:

class P {
public:
    Q q{ nullptr }; // BEWARE: You can NEVER use this object as it stands!
    int x;
//...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...