c ++ Функциональные объекты / указатель из любого класса [duplicate]

независимое от языка решение:

GIVEN: многоугольник может ВСЕГДА состоять из n-2 треугольников, которые не перекрываются (n = количество точек OR сторон). 1 треугольник = трехгранный многоугольник = 1 треугольник; 1 квадрат = 4-сторонний многоугольник = 2 треугольника; и т. д. ad nauseam QED

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

, если вы возьмете любые 3 последовательные точки в пути многоугольников и создаете треугольник с этими точками, вы будете имеют один и только один из трех возможных сценариев:

  1. полученный треугольник полностью находится внутри исходного многоугольника
  2. , в результате треугольник полностью выходит за пределы оригинального многоугольника
  3. , что приводит к треугольнику частично содержится в исходном многоугольнике

, нас интересуют только случаи, которые попадают в первый вариант (полностью содержащийся).

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

как реализовать это программно:

создать массив (последовательных) точек, которые представляют путь ВОКРУГ многоугольника. начните в точке 0. запустите массив, создавая треугольники (по одному за раз) из точек x, x + 1 и x + 2. преобразуйте каждый треугольник из формы в область и пересечь его с областью, созданной из многоугольника. Если полученное пересечение идентично исходному треугольнику, то указанный треугольник полностью содержится в многоугольнике и может быть отрублен. удалите x + 1 из массива и начните снова с x = 0. в противном случае (если треугольник находится вне [частично или полностью] многоугольника), переходите к следующей точке x + 1 в массиве.

дополнительно, если вы хотите интегрироваться с отображением и начинать с геоинтеграции, вы должны преобразовать из геоинформации на экранные точки FIRST. это требует решения моделирования и формулы для формы земли (хотя мы склонны думать о Земле как о шаре, это на самом деле нерегулярная яйцевидная (яйцо) с вмятинами). существует много моделей, для получения дополнительной информации wiki. важная проблема заключается в том, считаете ли вы район плоской или изогнутой. в общем, «маленькие» области, где точки до нескольких километров друг от друга, не будут вызывать значительную ошибку, если рассматривать плоские и не выпуклые.

83
задан Jon Seigel 16 April 2010 в 21:24
поделиться

9 ответов

91
ответ дан K Scott Piel 17 August 2018 в 14:13
поделиться
typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference
15
ответ дан AraK 17 August 2018 в 14:13
поделиться
  • 1
    Честно говоря, у меня нет компилятора между моими руками, и я просто ненавижу синтаксис указателей функций-членов :( – AraK 28 September 2009 в 09:37
  • 2
    Должно быть: (pDog- & gt; * doSomething) (); // если pDog является указателем // (pDog. * doSomething) (); // если pDog является ссылочным as () оператором имеет более высокий приоритет, то - & gt; * и. *. – Tomek 28 September 2009 в 10:13
  • 3
    @Tomek Большое спасибо :) – AraK 28 September 2009 в 12:34

Функциональный указатель на член класса - это проблема, которая действительно подходит для использования функции boost ::. Небольшой пример:

#include <boost/function.hpp>
#include <iostream>

class Dog 
{
public:
   Dog (int i) : tmp(i) {}
   void bark ()
   {
      std::cout << "woof: " << tmp << std::endl;
   }
private:
   int tmp;
};



int main()
{
   Dog* pDog1 = new Dog (1);
   Dog* pDog2 = new Dog (2);

   //BarkFunction pBark = &Dog::bark;
   boost::function<void (Dog*)> f1 = &Dog::bark;

   f1(pDog1);
   f1(pDog2);
}
5
ответ дан Benjamin 17 August 2018 в 14:13
поделиться

Минимальный пример runnable

#include <cassert>

class C {
    public:
        int i;
        C(int i) : i(i) {}
        int m(int j) { return this->i + j; }
};

int main() {
    // Get a method pointer.
    int (C::*p)(int) = &C::m;

    // Create a test object.
    C c(1);
    C *cp = &c;

    // Operator .*
    assert((c.*p)(2) == 3);

    // Operator ->*
    assert((cp->*p)(2) == 3);
}

Вы не можете изменить порядок скобок или опустить их. Следующие не работают:

c.*p(2)
c.*(p)(2)

C ++ 11 standard

.* и ->* - сингенные операторы , введенные в C ++ для эта цель и не представлена ​​в C.

C ++ 11 N3337 стандартная черновик :

  • 2.13 «Операторы и пунктуаторы» имеет список всех операторов, которые содержат .* и ->*.
  • 5.5 «Операторы« указатель-член »объясняют, что они делают
5
ответ дан Ciro Santilli 新疆改造中心 六四事件 法轮功 17 August 2018 в 14:13
поделиться

Чтобы создать новый объект, вы можете либо использовать новое место размещения, как упоминалось выше, либо реализовать свой метод clone (), создающий копию объекта. Затем вы можете вызвать этот метод clone с помощью указателя функции-члена, как описано выше, для создания новых экземпляров объекта. Преимущество клона заключается в том, что иногда вы можете работать с указателем на базовый класс, где вы не знаете тип объекта. В этом случае метод clone () может быть проще в использовании. Кроме того, clone () позволит вам скопировать состояние объекта, если это то, что вы хотите.

1
ответ дан Corwin Joy 17 August 2018 в 14:13
поделиться
  • 1
    клоны могут быть дорогими, и ОП, возможно, пожелает избежать их, если проблема является проблемой или какой-либо проблемой. – Eric 25 October 2012 в 18:58

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

template <class T> struct MethodHelper;
template <class C, class Ret, class... Args> struct MethodHelper<Ret(C::*)(Args...)> {
    using T = Ret (C::*)(Args...);
    template <T m> static Ret call(C* object, Args... args) {
        return (object->*m)(args...);
    }
};

#define METHOD_FP(m) MethodHelper<decltype(m)>::call<m>

Итак, для вашего примера вы теперь выполните:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)
6
ответ дан eyelash 17 August 2018 в 14:13
поделиться

Как получить указатель на функцию для функции-члена класса, а затем вызвать эту функцию-член с определенным объектом?

Легче всего начать с typedef , Для функции-члена вы добавляете имя класса в объявление типа:

typedef void(Dog::*BarkFunction)(void);

Затем, чтобы вызвать метод, вы используете оператор ->*:

(pDog->*pBark)();

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

Не думаю, что вы можете работать с такими конструкторами, как ctors и dtors. Обычный способ достижения такого рода вещей - использовать фабричный метод, который в основном представляет собой статическую функцию, которая вызывает конструктор для вас. Для примера см. Приведенный ниже код.

Я изменил ваш код, чтобы в основном описать то, что вы описали. Ниже приведены некоторые оговорки.

#include <iostream>

class Animal
{
public:

    typedef Animal*(*NewAnimalFunction)(void);

    virtual void makeNoise()
    {
        std::cout << "M00f!" << std::endl;
    }
};

class Dog : public Animal
{
public:

    typedef void(Dog::*BarkFunction)(void);

    typedef Dog*(*NewDogFunction)(void);

    Dog () {}

    static Dog* newDog()
    {
        return new Dog;
    }

    virtual void makeNoise ()
    {
        std::cout << "Woof!" << std::endl;
    }
};

int main(int argc, char* argv[])
{
    // Call member function via method pointer
    Dog* pDog = new Dog ();
    Dog::BarkFunction pBark = &Dog::makeNoise;

    (pDog->*pBark)();

    // Construct instance via factory method
    Dog::NewDogFunction pNew = &Dog::newDog;

    Animal* pAnimal = (*pNew)();

    pAnimal->makeNoise();

    return 0;
}

Теперь, хотя вы обычно можете использовать Dog* вместо Animal* благодаря магии полиморфизма, тип указателя функции делает не следуют правилам поиска иерархии классов. Таким образом, указатель метода Animal несовместим с указателем метода Dog, другими словами, вы не можете назначить Dog* (*)() переменной типа Animal* (*)().

Статический метод newDog является простой пример фабрики, который просто создает и возвращает новые экземпляры. Будучи статической функцией, она имеет регулярный typedef (без классификатора классов).

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

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

47
ответ дан gavinb 17 August 2018 в 14:13
поделиться
  • 1
    Я использовал C ++ более 10 лет и регулярно изучаю что-то новое. Раньше я никогда не слышал о ->*, но теперь надеюсь, что мне это никогда не понадобится :) – Thomas 28 September 2009 в 12:37

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

Чтобы вызвать функцию-член, вам нужно знать две вещи:

  • Какая функция-член для вызова
  • Какой экземпляр должен использоваться (функция-член)

Обычные указатели функций не могут хранить оба. Указатели функций элемента C ++ используются для хранения a), поэтому вам нужно явно указать экземпляр при вызове указателя функции-члена.

4
ответ дан hrnt 17 August 2018 в 14:13
поделиться
  • 1
    Я проголосовал за это, но добавил бы пояснения в том случае, если ОП не знает, к чему вы обращаетесь, например, «какой экземпляр». Я бы расширил, чтобы объяснить встроенный «этот» указатель. – Eric 25 October 2012 в 19:01

Я не думаю, что кто-то объяснил здесь, что одна проблема заключается в том, что вам нужны « указатели на элементы », а не обычные указатели функций.

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

Сказав, что они вам нужны, теперь я скажу, что вам действительно нужно их избегать. Серьезно, указатели членов - это боль. Гораздо более разумно смотреть на объектно-ориентированные шаблоны проектирования, которые достигают той же цели, или использовать boost::function или что-то еще, как упомянуто выше, - если вы сделаете этот выбор, то есть.

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

class myclass
{
  public:
    virtual void myrealmethod () = 0;

    static void myfunction (myclass *p);
}

void myclass::myfunction (myclass *p)
{
  p->myrealmethod ();
}

Поскольку myfunction - действительно просто нормальная функция (проблемы с областью действия), Функциональный указатель можно найти в обычном режиме C.

EDIT - этот метод называется «метод класса» или «статическая функция-член». Основное отличие от функции, не являющейся членом, заключается в том, что если вы ссылаетесь на нее вне класса, вы должны указать область с помощью оператора разрешения области ::. Например, чтобы получить указатель на функцию, используйте &myclass::myfunction и вызовите его, используя myclass::myfunction (arg);.

Подобные вещи довольно распространены при использовании старых API Win32, которые первоначально были предназначены для C, а чем C ++. Конечно, в этом случае параметр обычно является LPARAM или аналогичным, а не указателем, и требуется некоторое литье.

26
ответ дан Ziezi 17 August 2018 в 14:13
поделиться
  • 1
    «myfunction» не является нормой, если по нормали вы имеете в виду функцию стиля C. «myfunction» более точно называют методом myclass. Методы класса не похожи на обычные функции, поскольку они имеют что-то, что функция стиля C не является указателем «this». – Eric 25 October 2012 в 18:57
  • 2
    советуя использовать boost, драконов. Есть практические веские причины для использования указателей методов. Я не против упоминания о повышении в качестве альтернативы, но ненавижу, когда кто-то говорит, что кто-то другой должен использовать его, не зная всех фактов. Boost стоит дорого! И если это встроенная платформа, то это не может быть выбором, который возможен. Помимо этого, мне очень нравится писать. – Eric 25 October 2012 в 19:10
  • 3
    @ Эрик. На второй момент я не собирался говорить «использовать Boost», и на самом деле я никогда не использовал Boost. Намерение (насколько я знаю это после 3 лет) состояло в том, что люди должны искать альтернативы и перечислять несколько возможностей. "Или что угодно" указывает, что список не должен быть исчерпывающим. У указателей-указателей есть стоимость в удобочитаемости. Их краткое представление источника также может маскировать затраты времени исполнения - в частности, указатель участника на метод должен справляться как с не виртуальными, так и с виртуальными методами и должен знать, что именно. – Steve314 25 October 2012 в 23:09
  • 4
    @Eric. Не только это, но и эти проблемы являются причиной не-переносимости с указателями-членами. Visual C ++, по крайней мере в прошлом, нуждался в дополнительных подсказках о том, как представлять типы указателей-членов. Я бы использовал подход статической функции для встроенной системы - представление указателя совпадает с любым другим указателем функции, затраты очевидны, и проблема переноса невозможна. И вызов, обернутый статической функцией-членом, знает (во время компиляции), является ли вызов виртуальным или нет, - не требуется проверка времени выполнения за пределами обычных запросов vtable для виртуальных методов. – Steve314 25 October 2012 в 23:15
  • 5
    @Eric - в вашей первой точке - я знаю, что статическая функция-член не совсем такая же, как функция C-стиля (отсюда и «проблемы с областью»), но я, вероятно, должен был включить это имя. – Steve314 25 October 2012 в 23:23
Другие вопросы по тегам:

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