виртуальный C++ оператора присваивания

Я нахожу Func<T> очень полезным, когда создаю компонент, который нужно персонализировать «на лету».

Возьмем этот очень простой пример: компонент PrintListToConsole<T>.

Очень простой объект, который печатает этот список объектов на консоли. Вы хотите, чтобы разработчик, который использует его, персонализировал вывод.

Например, вы хотите дать ему определение определенного типа номера и т. Д.

Без Func

g7]

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

interface PrintListConsoleRender<T> {
  String Render(T input);
}

Затем вам нужно создать класс PrintListToConsole<T> который принимает ранее созданный интерфейс и использует его по каждому элементу списка.

class PrintListToConsole<T> {

    private PrintListConsoleRender<T> _renderer;

    public void SetRenderer(PrintListConsoleRender<T> r) {
        // this is the point where I can personalize the render mechanism
        _renderer = r;
    }

    public void PrintToConsole(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderer.Render(item));
        }
    }   
}

Разработчик, которому необходимо использовать ваш компонент, должен:

  1. реализовать интерфейс
  2. передают действительный класс в PrintListToConsole
    class MyRenderer : PrintListConsoleRender<int> {
        public String Render(int input) {
            return "Number: " + input;
        }
    }
    
    class Program {
        static void Main(string[] args) {
            var list = new List<int> { 1, 2, 3 };
            var printer = new PrintListToConsole<int>();
            printer.SetRenderer(new MyRenderer());
            printer.PrintToConsole(list);
            string result = Console.ReadLine();   
        }   
    }
    

. Использование Func намного проще

. Внутри компонента вы определяете параметр type Func<T,String>, который представляет собой интерфейс функции, которая принимает входной параметр типа T и возвращает строку (вывод для консоли)

class PrintListToConsole<T> {

    private Func<T, String> _renderFunc;

    public void SetRenderFunc(Func<T, String> r) {
        // this is the point where I can set the render mechanism
        _renderFunc = r;
    }

    public void Print(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderFunc(item));
        }
    }
}

Когда разработчик использует ваш компонент, он просто переходит к компонент реализует тип Func<T, String>, то есть функционал на котором создается вывод для консоли.

class Program {
    static void Main(string[] args) {
        var list = new Array[1, 2, 3];
        var printer = new PrintListToConsole<int>();
        printer.SetRenderFunc((o) => "Number:" + o);
        printer.Print();
        string result = Console.ReadLine();
    }
}

Func<T> позволяет вам определять общий интерфейс метода «на лету». Вы определяете тип ввода и тип вывода. Простой и лаконичный.

65
задан Brian R. Bondy 21 March 2009 в 20:42
поделиться

4 ответа

Оператор присваивания не требуется, чтобы быть сделанным виртуальным.

обсуждение ниже - приблизительно operator=, но оно также относится к любому оператору, перегружающемуся, который берет в рассматриваемом типе, и любая функция, которая берет в рассматриваемом типе.

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

<час>

Виртуальные функции не знают о наследовании параметра:

подпись функции А должна быть тем же для виртуального, чтобы сыграть роль. Таким образом даже при том, что в следующем примере, оператор = сделан виртуальным, вызов никогда не будет действовать как виртуальная функция в D, потому что параметры и возвращаемое значение оператора = отличаются.

функция B::operator=(const B& right) и D::operator=(const D& right) на 100% полностью отличается и рассматривается как 2 отличных функции.

class B
{
public:
  virtual B& operator=(const B& right)
  {
    x = right.x;
    return *this;
  }

  int x;

};

class D : public B
{
public:
  virtual D& operator=(const D& right)
  {
    x = right.x;
    y = right.y;
    return *this;
  }
  int y;
};
<час>

Значения по умолчанию и перегрузка 2 операторы:

Вы можете, хотя определяют виртуальную функцию, чтобы позволить Вам устанавливать значения по умолчанию для D, когда он присвоен переменной типа B. Это - то, даже если Ваша переменная B является действительно D, сохраненным в ссылку B. Вы не доберетесь эти D::operator=(const D& right) функция.

В ниже случая, присвоения от 2 объектов D, хранивших в 2 ссылках B... эти D::operator=(const B& right), переопределение используется.

//Use same B as above

class D : public B
{
public:
  virtual D& operator=(const D& right)
  {
    x = right.x;
    y = right.y;
    return *this;
  }


  virtual B& operator=(const B& right)
  {
    x = right.x;
    y = 13;//Default value
    return *this;
  }

  int y;
};


int main(int argc, char **argv) 
{
  D d1;
  B &b1 = d1;
  d1.x = 99;
  d1.y = 100;
  printf("d1.x d1.y %i %i\n", d1.x, d1.y);

  D d2;
  B &b2 = d2;
  b2 = b1;
  printf("d2.x d2.y %i %i\n", d2.x, d2.y);
  return 0;
}

Печать:

d1.x d1.y 99 100
d2.x d2.y 99 13

, Который показывает, что D::operator=(const D& right) никогда не используется.

Без виртуального ключевого слова на [1 110] у Вас были бы те же результаты как выше, но значение y не будет инициализировано. Т.е. это использовало бы B::operator=(const B& right)

<час>

Один последний шаг для связывания всего этого, RTTI:

можно использовать RTTI для надлежащей обработки виртуальных функций, которые берут в типе. Вот последняя часть загадки, которая выяснит, как правильно обработать присвоение при контакте с возможно наследованными типами.

virtual B& operator=(const B& right)
{
  const D *pD = dynamic_cast<const D*>(&right);
  if(pD)
  {
    x = pD->x;
    y = pD->y;
  }
  else
  {
    x = right.x;
    y = 13;//default value
  }

  return *this;
}
47
ответ дан Alberto Schiabel 7 November 2019 в 11:45
поделиться

Оператор является методом со специальным синтаксисом. Можно рассматривать его как любой другой метод...

3
ответ дан dmckee 7 November 2019 в 11:45
поделиться

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

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

Эта страница предоставляет превосходное и подробное описание того, как все это работает.

4
ответ дан sblom 7 November 2019 в 11:45
поделиться

Это зависит от оператора.

точка создания виртуального оператора присваивания должна позволить Вам от преимущества способности переопределить его для копирования большего количества полей.

Поэтому, если у Вас есть Base& и у Вас на самом деле есть Derived& как динамический тип и Полученный имеет больше полей, корректные вещи копируются.

Однако существует тогда риск, что Ваш LHS является Полученным, и RHS является Основа, поэтому когда выполнения виртуального оператора в Полученном, Ваш параметр не является Полученным и у Вас нет способа вытащить поля из него.

Вот хороший discussio: http://icu-project.org/docs/papers/cpp_report/the_assignment_operator_revisited.html

24
ответ дан Uri 7 November 2019 в 11:45
поделиться
Другие вопросы по тегам:

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