Objective-C подобная категории конструкция или техника в С++?

Функция категории

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

Могу ли я архивировать аналогичную функциональность (языковая конструкция или какой-либо метод) на С++?

Основной проблемой является согласованный метод вызова синтаксиса (. или -> operator).

Ответ 1

Рассмотрим следующий класс:

struct A {
    int x, y;
    A(int x, int y) : x(x), y(y) {}
};

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

С семантикой перемещения С++ 11 продвижение экземпляра A в подкласс B (наследование A) будет эффективным и не требует копирования экземпляра A:

class B : public A {
public:
    B (A &&a) : A(a), someOtherMember(a.x + a.y) {}

    // added public stuff:
    int someOtherFunction() const { return someOtherMember; }

private:
    // added private stuff:
    int someOtherMember;
};

Полный код с примером: http://ideone.com/mZLLEu

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

Обратите внимание на конструктор B (A &&a), который я называю "содействую конструктору" (это не стандартный термин). Обычно B (B &&b) - это конструктор перемещения, который перемещает содержимое предоставленного экземпляра B в новый B, который будет создан. Я использую семантику перемещения, чтобы переместить экземпляр A (который был возвращен другой функцией) в суперкласс A из B.

Эффективно, вы можете продвигать A до B, позволяя использовать B как A.

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

Ответ 2

Другой вариант, который не может рассматриваться как "чистый" некоторыми (хотя это, на мой взгляд), но все же выполняет то же самое, использует статический класс. Важно помнить, что когда мы создаем функцию-член, то, что действительно происходит за кулисами, заключается в том, что компилятор генерирует функцию, где первым является параметр (aka "this"). Таким образом, мы можем сделать то же самое, чтобы расширить функциональность нашего класса, не получая от него.

class Something
{
public:
   Something()
   ~Something()
}

// In objective-c you may call this category Something+Utils
class SomethingUtils
{
   // You can use a pointer, or a reference here, your call.
   static int GetSomethingElse(Something *something, int parameter);
}

Это приведет к тому же намерению, что и к категории: вы расширяете функциональные возможности своего объекта класса, и вам не нужно создавать новый производный класс. Вы не сможете получить доступ к частным или защищенным функциям и переменным, но вы не можете сделать это в objective-c, так что на этом фронте ничего не потеряно (и если вы пытаетесь использовать закрытое или защищенное состояние члена, вы "я пропустил точку в категориях целиком). Вы не сможете использовать. и → , но, на мой взгляд, гораздо лучший компромисс, чем вывод нового типа, чтобы добавить некоторые полезные методы.

Ответ 3

У С++ есть наследование для этого. Кроме того, я несколько раз использовал следующий трюк, чтобы расширить классы, созданные директивой #import "progid:...":

// This one is part of external framework, or auto-generated, or dllimport, or #import, etc..
class A
{
    protected double m_x;
};

// This one is the extension class. Make sure you only add non-virtual methods here.
// Static methods and static data members are OK as well.
class __declspec( novtable ) B: public A
{
public:
    double getSquare(){ return m_x * m_x; }
    __declspec( property( get = getSquare ) ) double square;
};

// Usage example
double someFunc( A& arg )
{
    B& b = static_cast<B&>( arg ); // Note we've not constructed any instance of B, just casted.
    return b.square;
}

Ответ 4

Я получил почти согласованное соглашение о вызове с идеей, о которой я говорил в молниеносной речи год назад:

(введение не имеет смысла без комментариев - подождите, пока он не дойдет до бита С++).

Обратите внимание, что материал не предназначен для серьезного рассмотрения, хотя некоторые из них неизбежно имеют; -)