Функция категории Objective C позволяет программисту добавлять новый метод, который не был определен в оригинальном определении класса.
Могу ли я заархивировать подобную функциональность (языковую конструкцию или некоторую технику) на C ++?
Основная проблема заключается в согласованном синтаксисе вызова метода (.
или же ->
оператор).
Давайте рассмотрим следующий класс для расширения:
struct A {
int x, y;
A(int x, int y) : x(x), y(y) {}
};
Вы можете наследовать от этого класса или написать класс-оболочку, который содержит экземпляр этого класса. В большинстве случаев наследование — это путь, как класс-обертка не А, но это обертывания (содержит) А.
С C ++ 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
,
В отличие от ответа Сунтса, мое решение также работает с добавленными виртуальными таблицами, поскольку оно не основано на небезопасном приведении указателей.
Другой вариант, который некоторые могут не рассматривать как «чистый» (хотя это, на мой взгляд), но все же выполняет то же самое, — это использование статического класса. Важно помнить, что когда мы создаем функцию-член, то, что действительно происходит за кулисами, так это то, что компиляция генерирует функцию, в которой объект (он же «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);
}
Это достигнет того же намерения, что и категория: вы расширяете функциональные возможности вашего объекта класса, и вам не нужно создавать новый производный класс. Вы не сможете получить доступ к закрытым или защищенным функциям-членам и переменным, но вы все равно не сможете сделать это в target-c, так что на этом фронте ничего не потеряно (и если вы пытаетесь использовать закрытое или защищенное государство-член, вы упустил пункт категории полностью). Вы не сможете использовать. и -> операторы, но, на мой взгляд, это гораздо лучший компромисс, чем вывод нового типа просто для добавления некоторых служебных методов.
C ++ имеет наследство для этого. Кроме того, я несколько раз использовал следующий трюк для расширения классов, сгенерированных директивой #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;
}
Я получил около согласованное соглашение о вызовах с идея, о которой я говорил в молниеносной беседе год или около того назад:
(введение не имеет смысла без комментариев — подождите, пока оно не дойдет до бита C ++).
Обратите внимание, что материал не был предназначен для всерьез — хотя некоторые неизбежно имеют 😉