изменить адрес объекта, который хранит необработанный указатель на его поле - PullRequest
0 голосов
/ 09 октября 2019

Я хочу перераспределить класс, который имеет собственную ссылку с именем CarJoker.
«Перераспределить» здесь означает = изменить адрес объекта.

Этот метод необходим для того, чтобы каждый экземпляр CarJoker жил в непрерывном массиве с изменяемым размером, например, в пуле.

Я думаю об использовании std::move, но он не может двигаться CarJoker::wheelsтак, как я хочу.
( MCVE )

#include <vector>
#include <iostream>
#include <string>
struct Wheel{
    void setBlade(){}
    void setTwinkle(){}
};
struct CarJoker{
    Wheel wa;
    Wheel wb;
    Wheel wc;
    std::vector<Wheel*> wheels;
    float hp=5;
    CarJoker(){
        wheels.push_back(&wa);
        wheels.push_back(&wb);
        wheels.push_back(&wc);
    }
    void wow(){
        //v want to apply something to every "wheel"
        for(auto wheel:wheels){
            wheel->setBlade();
        }
        //v want to apply something to some certain "wheel"
        wa.setTwinkle();
    }
};

int main(){
    CarJoker car1;
    CarJoker car2=std::move(car1);
    std::cout<<"want to 1 : "<<(car2.wheels[0]== &car2.wa)<<std::endl;
}

С std::move, car2.wheels[0] указывает на &car1.wa, а не &car2.wa, как я хочу.
Я знаю причину, но это не моя цель, и я не знаю элегантного способа ее исправить.

Мой плохой обходной путь

Вот нелегкий способ ( MCVE ): -

struct CarJoker{
    Wheel wa;
    Wheel wb;
    Wheel wc;
    std::vector<Wheel*> wheels;
    float hp=5;
    CarJoker(){
        reIni();  //: CHANGE (call a new function)
    }
    void reIni(){ //: CHANGE (create a new function)
        wheels.clear();
        wheels.push_back(&wa);
        wheels.push_back(&wb);
        wheels.push_back(&wc);
    }
    void wow(){
        //v want to apply something to every "wheel"
        for(auto wheel:wheels){
            wheel->setBlade();
        }
        //v want to apply something to some certain "wheel"
        wa.setTwinkle();
    }
};
int main(){
    CarJoker car1;
    CarJoker car2=std::move(car1);
    car2.reIni(); //: CHANGE (call a new function)
    std::cout<<"want to 1 : "<<(car2.wheels[0]== &car2.wa)<<std::endl;
}

Недостаток: -
1. Это грязно.
2. Мне нужно создать функцию специального имени (reIni()) для каждого класса, у которого есть такой симптом, который живет в пуле. Мой пул тоже должен распознавать эту функцию (например, зарегистрироваться с помощью шаблона или виртуальной функции).

Мой плохой обходной путь 2

 struct CarJoker{
    Wheel wa;
    Wheel wb;
    Wheel wc;
    std::vector<Wheel*> getWheels(){  //use this instead of "wheels"
        std::vector<Wheel*> re;
        re.push_back(&wa);
        re.push_back(&wb);
        re.push_back(&wc);
        return re;
    }
    ....
 }

Я бы работал, но я чувствую, что обходной путь такой сумасшедший.
Ограничение увеличивает ловушку для кодировщиков.

Если wheels требуется кэшировать результат, требующий больших затрат на вычисления, то теперь было бы дорого вызывать getWheels() часто.

1 Ответ

5 голосов
/ 09 октября 2019

Вам не нужен метод reIni(). Вам нужно добавить:

  • конструктор копирования и конструктор перемещения, которые оба инициализируют элемент wheels так же, как конструктор по умолчанию.

  • оператор назначения копирования и оператор назначения перемещения, которые не копируют / не перемещают элемент wheels.

Попробуйте это:

struct CarJoker{
    Wheel wa;
    Wheel wb;
    Wheel wc;
    std::vector<Wheel*> wheels;
    float hp = 5;

    CarJoker(){
        wheels.push_back(&wa);
        wheels.push_back(&wb);
        wheels.push_back(&wc);
    }

    CarJoker(const CarJoker &src) :
        CarJoker(),
        wa(src.wa),
        wb(src.wb),
        wc(src.wc),
        //wheels(src.wheels),
        hp(src.hp){
    }

    CarJoker(CarJoker &&src) :
        CarJoker(),
        wa(std::move(src.wa)),
        wb(std::move(src.wb)),
        wc(std::move(src.wc)),
        //wheels(std::move(src.wheels)), 
        hp(src.hp){
    }

    // copy assignment and move assignment can be
    // handled with a single implementation that
    // lets the compiler choose between the copy
    // constructor and move constructor as needed...
    CarJoker& operator=(CarJoker rhs){
        wa = std::move(rhs.wa);
        wb = std::move(rhs.wb);
        wc = std::move(rhs.wc);
        wheels = std::move(rhs.wheels);
        hp = rhs.hp;
        return *this;
    }

    ...
}; 

При этом вам не следует начинать использовать поля со ссылками на себя. Одно поле std::array<Wheel, 3> имеет больше смысла, чем поле 3 Wheel и поле std::vector<Wheel*>, что позволяет избежать всей этой проблемы.

struct CarJoker{
    std::array<Wheel, 3> wheels;
    float hp = 5;

    CarJoker() = default;
    CarJoker(const CarJoker&) = default;
    CarJoker(CarJoker&&) = default;
    CarJoker& operator=(const CarJoker&) = default;
    CarJoker& operator=(CarJoker&&) = default;

    Wheel& wa() { return wheels[0]; }
    const Wheel& wa() const { return wheels[0]; }

    Wheel& wb() { return wheels[1]; }
    const Wheel& wb() const { return wheels[1]; }

    Wheel& wc() { return wheels[2]; }
    const Wheel& wc() const { return wheels[2]; }

    void wow(){
        //v want to apply something to every "wheel"
        for(auto &wheel : wheels){
            wheel.setBlade();
        }

        //v want to apply something to some certain "wheel"
        wa().setTwinkle();
    }
};
...