Принуждение чего-то быть разрушенным в последний раз в C++

Основная проблема в том, что вы используете индекс массива как key. При первом отображении элементов ListItem у вас есть:

  • Имя ListItem = {'Tobi'} ключ = {0}
  • Имя ListItem = {'Maxi'} ключ = {1}
  • Имя ListItem = {'David'} ключ = {2}
  • Имя ListItem = {'Peter'} ключ = {3}

Допустим, вы удалил элемент с индексом 1, все остальные элементы будут смещены в индекс:

  • ListItem name = {'Tobi'} key = {0}
  • ListItem name = {'David'} key = {1}
  • ListItem name = {'Peter'} key = {2}

React будет сравнивать только ключи, и поскольку единственная разница между первым и вторым рендер состоит в том, что элемент с ключом = {3} отсутствует, это элемент, который будет удален из домена.

Также избегайте прямого изменения состояния (few.splice(id,1)) и старайтесь избегать this.forceUpdate()

Попробуйте использовать фактический идентификатор в ваших данных:

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class List extends Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [
        { id: 1, name: "Tobi" },
        { id: 2, name: "Maxi" },
        { id: 3, name: "David" },
        { id: 4, name: "Peter" }
      ]
    };
  }

  removeItem = id => {
    let few = this.state.items.filter(item => item.id !==id);
    //console.log(this.state.items);
    this.setState({ items: few }, function() {
      console.log(this.state.items.map(item => item.name));
      //this.forceUpdate();
    });
  };

  render() {
    return (
      <div>
        <ul>
          {this.state.items.map((item, i) => (
            <ListItem
              name={item.name}
              key={item.id}
              id={item.id}
              remove={this.removeItem}
            />
          ))}
        </ul>
      </div>
    );
  }
}

class ListItem extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: this.props.name,
      id: this.props.id
    };
  }

  test = () => {
    this.props.remove(this.state.id);
  };

  render() {
    return (
      <li>
        {this.state.name} <button onClick={() => this.test()}>click me</button>
      </li>
    );
  }
}


function App() {
  return (
    <div className="App">
      <List />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
6
задан Steven 13 November 2008 в 20:49
поделиться

15 ответов

Вы могли использовать шаблон The Observer. Контроллер связывается с, он - супервизор, что это уничтожается. И Супервизор передает то же к, он - ребенок после разрушения.

Смотрите на http://en.wikipedia.org/wiki/Observer_pattern

2
ответ дан 8 December 2019 в 17:29
поделиться

когда я прочитал заголовок в этот вопрос, я сразу спросил меня, "Если был способ, которым какой-либо объект мог гарантировать, что был уничтожен (разрушенный?) в последний раз, затем что произошло бы, если бы два объекта приняли тот метод?"

0
ответ дан 8 December 2019 в 17:29
поделиться

Стандарт C++ указывает порядок инициализации/разрушения, когда рассматриваемые переменные все помещаются в один файл ("единица перевода"). Что-либо, что охватывает больше чем один файл, становится непортативным.

Я был бы второй, предложения, чтобы иметь Супервизор уничтожают каждый контроллер. Это ориентировано на многопотоковое исполнение в том смысле, что только Супервизор говорит кому-либо уничтожать себя (никто не уничтожает себя самостоятельно), таким образом, нет никакого состояния состязания. Необходимо было бы также избежать любых возможностей мертвой блокировки (подсказка: удостоверьтесь, что контроллеры могут уничтожить себя, после того как им сказали, не нуждаясь ни в чем больше от Супервизора).


Возможно сделать это ориентированным на многопотоковое исполнение, даже если контроллер должен быть уничтожен до конца программы (то есть, если контроллеры могут быть недолгими), затем они (или кто-то еще).

Во-первых, это не может быть состояние состязания для волнения о том, если я решаю уничтожить меня, и несколько микросекунд спустя Супервизор решает уничтожить меня и говорит мне так.

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

0
ответ дан 8 December 2019 в 17:29
поделиться

Сделайте cotrol супервизор одиночным элементом. Удостоверьтесь, что конструктор управления получает супервизор во время конструкции (не послесловия). Это гарантирует, что супервизор управления полностью создается перед управлением. Thuse деструктор назовут перед деструктором супервизора управления.

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };
0
ответ дан 8 December 2019 в 17:29
поделиться

Можно использовать События для передачи сигналов о разрушении Контроллеров

Добавьте WaitForMultipleObjects в деструкторе Супервизора, который будет ожидать, пока все контроллеры не будут уничтожены.

В деструкторе контроллеров Вы можете, сгенерировал событие Exit контроллера.

Вы должны поддержать глобальный массив дескрипторов события Exit для каждого контроллера.

0
ответ дан 8 December 2019 в 17:29
поделиться

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

Используйте соответствующую защиту (theading, и т.д.) вокруг одиночного элемента при необходимости.

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
        controller_(NULL)
        {
            controller_ = Controller::create();
        }

    ~ControllerClient() {
        delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {        
        if (!theSupervisor ) {
            theSupervisor = Supervisor();
        }
        return *theSupervisor;
    } 

    void deregister(Controller& controller) {
        remove( controller );
        if( controllers_.empty() ) {
            theSupervisor = NULL;
            delete this;
        }       
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
        return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
        supervisor_->deregister(*this);
        supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
        supervisor_(&supervisor)
        {}
}
0
ответ дан 8 December 2019 в 17:29
поделиться

В то время как ужасный это могло бы быть самым простым методом:

просто поместите выгоду попытки вокруг вызова нерегистра. Вы не должны изменять много кода и так как приложение уже закрывается, это не грандиозное предприятие. (Или есть ли другие ратификации для порядка завершения работы?)

Другие указали на лучшие проекты, но этот прост. (и ужасный)

Я предпочитаю шаблон "наблюдатель" также в этом случае.

0
ответ дан 8 December 2019 в 17:29
поделиться

Как насчет того, чтобы иметь супервизор заботятся о разрушении контроллеров?

0
ответ дан 8 December 2019 в 17:29
поделиться

GNU gcc/g ++ обеспечивает не портативные атрибуты для типов, которые очень полезны. Один из этих атрибутов является init_priority, который определяет порядок, в котором глобальные объекты создаются и, как следствие, обратный порядок, в котором они разрушены. От человека:

init_priority (ПРИОРИТЕТ)

 In Standard C++, objects defined at namespace scope are guaranteed
 to be initialized in an order in strict accordance with that of
 their definitions _in a given translation unit_.  No guarantee is
 made for initializations across translation units.  However, GNU
 C++ allows users to control the order of initialization of objects
 defined at namespace scope with the init_priority attribute by
 specifying a relative PRIORITY, a constant integral expression
 currently bounded between 101 and 65535 inclusive.  Lower numbers
 indicate a higher priority.

 In the following example, `A' would normally be created before
 `B', but the `init_priority' attribute has reversed that order:

      Some_Class  A  __attribute__ ((init_priority (2000)));
      Some_Class  B  __attribute__ ((init_priority (543)));

 Note that the particular values of PRIORITY do not matter; only
 their relative ordering.
1
ответ дан 8 December 2019 в 17:29
поделиться

Вы могли сделать любое следующее согласно обстоятельствам.

  1. Используйте шаблон The Observer, как предложено gurin. В основном супервизор сообщает контроллерам, что понижается...
  2. Имейте Супервизор, "владеют" Контроллерами и ответственны за их разрушение, когда это понижается.
  3. Сохраните Контроллеры в shared_pointers поэтому, кто бы ни спускается, в последний раз сделает реальное разрушение.
  4. Управляйте обоими (интеллектуальные указатели к) контроллеры и супервизор на стеке, который позволит Вам определять порядок разрушения
  5. другие...
0
ответ дан 8 December 2019 в 17:29
поделиться

Вы могли посмотреть на использование количества зарегистрированных контроллеров как сигнальная метка для фактического удаления.

Удалить вызов является затем просто запросом, и необходимо ожидать, пока контроллеры не вычеркивают из списка.

Как упомянуто это - одно использование шаблона "наблюдатель".

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
        inDeleteMode_ = true;
        if( controllers_.empty()){
            delete this;
        }
    }

    void deregister(Controller* controller) {
        controllers_.erase(
            remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
        if( inDeleteMode_ && controllers_.empty()){
            delete this;
        }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();
0
ответ дан 8 December 2019 в 17:29
поделиться

Существует в основном целая глава по этой теме в современном Дизайне C++ Alexandrescu (Chaper 6, Одиночные элементы). Он определяет singleton-класс, который может управлять зависимостями, даже между самими одиночными элементами.

Целая книга настоятельно рекомендована также BTW.

5
ответ дан 8 December 2019 в 17:29
поделиться

Несколько предложений:

  • сделайте controllerSupervisor одиночным элементом (или перенеситесь, он в одиночном элементе возражает, что Вы создаете с этой целью), к этому получают доступ через статический метод, который возвращает указатель, затем dtors зарегистрированных объектов может назвать статическое средство доступа (который в случае завершения работы приложения и controllerSupervisor был уничтожен, возвратит ПУСТОЙ УКАЗАТЕЛЬ), и те объекты могут постараться не называть вычеркивать из списка метод в этом случае.

  • создайте controllerSupervisor на "куче", использующей новый, и используйте что-то как boost::shared_ptr<> управлять его временем жизни. Раздайте shared_ptr<> в статическом методе доступа одиночного элемента.

1
ответ дан 8 December 2019 в 17:29
поделиться

Порядок разрушения автоматических переменных (которые включают "нормальные" локальные переменные, которые Вы используете в функциях) находится в обратном порядке их создания. Так поместите controllerSupervisor наверху.

Порядок разрушения globals находится также в реверсе их создания, которое в свою очередь зависит от порядка, в котором они определяются: Позже определенные объекты создаются позже. Но остерегайтесь: Объекты, определенные в различных .cpp файлах (единицы перевода), не гарантируются созданному ни в каком определенном порядке.

Я думаю, что необходимо рассмотреть использование его, как Mike рекомендовал:

  1. Создание сделано при помощи шаблона "одиночка" (так как порядок инициализации объектов в различных единицах перевода не определяется) на первом использовании, путем возврата указателя на функционально-статический объект супервизора.
  2. Супервизор обычно разрушается (использование правил о разрушении помех в функциях). контроллеры вычеркивают из списка использование статической функции супервизора. Тот проверяет, разрушен ли супервизор уже (проверка указателя для != 0). Если это, то ничто не сделано. Иначе супервизор уведомляется.

Так как я предполагаю, что мог быть супервизор без подключенного контроллера (и если только временный), интеллектуальный указатель не мог бы использоваться для разрушения супервизора автоматически.

5
ответ дан 8 December 2019 в 17:29
поделиться

Это не точно изящно, но Вы могли сделать что-то вроде этого:

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

Новый глобальный:

ControllerCoordinator control;

Везде Вы создаете контроллер, добавляете control.supervisor.insert(controller). Везде Вы уничтожаете один, добавляете control.erase(controller). Вы смогли избегать control. префикс путем добавления глобальной ссылки на control.supervisor.

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

0
ответ дан 8 December 2019 в 17:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: