Расширение С++
От: Denwer Россия  
Дата: 24.03.03 06:48
Оценка: 3 (1)
Привет всем.
Вот у меня какое предложение.
Очень часте приходится например добавлять какуе то функцию, для класса, при этом приходится ее делать либо как член класса, тогда нужно еще переписать все конструкторы и оператор присваивания, либо не как член класса, тогда все протектед данные будут не видны.
Так вот, хорошо бы добавить в С++ такую фичу.
class A
{
..........
}

extend class A
{
//тут методы и данные которые добавляются к классу А
//причем тип доступа такойже как и при наследовании
//тоесть можно добраться до протектед данных
}


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

Какие есть мнения на сей счет???
Re: Расширение С++
От: Аноним  
Дата: 24.03.03 08:11
Оценка:
Это получается, что ты хочешь иметь объекты А переменного размера?
Т.е.
class A{
public:
   virtual void Fun(){}
private:
   double d;
};
A a; //sizeof a == 16
extended class A{
public:
   void Fun1(){
      i = 10; 
   }
private:
  int i;
};



void g(){ //sizeof a == 24 ???????
   a.Fun1(); ?????
}


А может лучше о наследовании подумать?
Re[2]: Расширение С++
От: Denwer Россия  
Дата: 24.03.03 08:24
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А может лучше о наследовании подумать?


Ну в начале можн хотябы вообще запрятить объявление виртуальных методов.
А вообще над этим тоже можно подумать еще. Ну например(первое что пришло в голову) иметь не одну виртуальную таблицу а несколько, причем последний элемент этой таблицы будет указатель на следующую. Конечно работать будет медленнее, но это решение так скажем "в лоб", и что стоит ЕЩЕ подумать.

О наследовании думай сколько хочешь, но если ты объект не создаешь то никакое наследование не поможет. Только обычную функцию писать, которая конечно не доберется до протектед методов.
Re: Расширение С++
От: Sergeem Израиль  
Дата: 24.03.03 09:23
Оценка:
Здравствуйте, Denwer, Вы писали:

D>Привет всем.

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

Вот это я не понял! Зачем переписывать конструкторы, если мы добавляем новую функцию в класс??!

Может быть автор имеет желание скрыть реализацию класса, так чтобы ее не было видно в интерфейсном хедере? Тогда надо воспользоваться pimpl-ом.
Serge.

Hасколько проще была бы жизнь, если бы она была в исходниках.
Re[2]: Расширение С++
От: Denwer Россия  
Дата: 24.03.03 09:36
Оценка:
Здравствуйте, Sergeem, Вы писали:

S>Здравствуйте, Denwer, Вы писали:


D>>Привет всем.

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

S>Вот это я не понял! Зачем переписывать конструкторы, если мы добавляем новую функцию в класс??!


Мож я неправельно выразился: не переписывать реализацию конструкторов, а написать свои конструкторы которые вызывают конструкторы базового класса. В С++ не наследуются конструкторы и оператор присваивания. Или я уже отстал от жизни настолько сильно?

S>Может быть автор имеет желание скрыть реализацию класса, так чтобы ее не было видно в интерфейсном хедере? Тогда надо воспользоваться pimpl-ом.


Чем-чем?
Re[3]: Расширение С++
От: Znow  
Дата: 24.03.03 09:57
Оценка:
Здравствуйте, Denwer, Вы писали:

D>В С++ не наследуются конструкторы и оператор присваивания.


Операторы присваивания наследуются (но по умолчанию их имена скрыты).
Re: Расширение С++
От: Аноним  
Дата: 24.03.03 09:57
Оценка:
Здравствуйте, Denwer, Вы писали:

http://www.eclipse.org/aspectj/
http://www.aspectc.org/
Может, это то, что ты ищешь?

--
Дмитрий
Re: Расширение С++
От: FatZorro  
Дата: 24.03.03 10:43
Оценка:
Здравствуйте, Denwer, Вы писали:

D>Привет всем.

D>Вот у меня какое предложение.
D>Очень часте приходится например добавлять какуе то функцию, для класса, при этом приходится ее делать либо как член класса, тогда нужно еще переписать все конструкторы и оператор присваивания, либо не как член класса, тогда все протектед данные будут не видны.
D>Так вот, хорошо бы добавить в С++ такую фичу.

D>class A

D>{
D>..........
D>}

D>extend class A


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

PS Хотя косяк! ClassName.Method() так то не получится
Re[2]: Расширение С++
От: Denwer Россия  
Дата: 24.03.03 11:50
Оценка:
Здравствуйте, FatZorro, Вы писали:


FZ>А почему нельзя добавить в класс дружественную функцию? Может я чего не понял?

FZ>Т.е. понядобился тебе в классе новый метод, взял и добавил описание ее как дружественной....

FZ>PS Хотя косяк! ClassName.Method() так то не получится


В основном то реч идет не о моих классах. И что мне взять чужие хедеры и добавить туда строчку с дружественным объявлением. Если бы это мои классы и я мог бы это сделать, то все же я бы не морочил себе голову и сделал наследование.
Re[2]: Расширение С++
От: Аноним  
Дата: 24.03.03 13:06
Оценка: 12 (1)
Здравствуйте, Sergeem, Вы писали:

S>Здравствуйте, Denwer, Вы писали:


D>>Привет всем.

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

S>Вот это я не понял! Зачем переписывать конструкторы, если мы добавляем новую функцию в класс??!


Я тоже не уверен, что понял Denwer'а правильно, но если что, он меня поправит.

Допустим, один разработчик разработал библиотеку классов A, B, C, D... (и, для определенности, класс A является базовым классом иерархии). А другой разработчик решил ее использовать. И все ему в этой библиотеке нравится, но... если бы класс A имел дополнительно еще метод do_something(), который делает очень полезные вещи, было бы совсем супер! Можно конечно сделать свой класс X, который наследуется от A и расширяет функциональность желанным методом do_something(), но классы B, C, D (которые наследуются от A, а не от X) не получат желаемой функциональности. Можно взять исходники библиотеки и добавить этот злосчастный метод прямо в A. Но, по-первых, исходники могут быть недоступны, а во-вторых, первый разработчик может продолжительное время сопровождать свою библиотеку исправляя и добавляя баги и фичи, и с каждым новым релизом библиотеки, второму разработчику приходится заново все добавлять.

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

Denwer, я правильно понял?

--
Дмитрий
Re[3]: Расширение С++
От: Denwer Россия  
Дата: 24.03.03 13:54
Оценка:
Здравствуйте, Аноним, Вы писали:


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


А>Denwer, я правильно понял?


А>--

А>Дмитрий

ДА ДА ДА
Re: Расширение С++
От: Chorkov Россия  
Дата: 24.03.03 14:57
Оценка:
Здравствуйте, Denwer, Вы писали:

D>Привет всем.

D>Вот у меня какое предложение.
D>Очень часте приходится например добавлять какуе то функцию, для класса, при этом приходится ее делать либо как член класса, тогда нужно еще переписать все конструкторы и оператор присваивания, либо не как член класса, тогда все протектед данные будут не видны.
D>Так вот, хорошо бы добавить в С++ такую фичу.

D>class A

D>{
D>..........
D>}

D>extend class A

D>{
D>//тут методы и данные которые добавляются к классу А
D>//причем тип доступа такойже как и при наследовании
D>//тоесть можно добраться до протектед данных
D>}

D>Причем мы не вводим нового класаа, а это иногда большой плюс если объект создает какая нибудь библиотека, то добавлять функции приходится не как члены класа. Взять ту же MFC, когда нужно дописать новый метод для класса CWnd, экземпляр которого создан самой библиотекой, то начинаешь шаманить.


D>Какие есть мнения на сей счет???



А такли нужено оформлять собственную функцию в виде метода класса?
На основе стати http://www.softcraft.ru/coding/sm/sm01.shtml ,
можносказать, что добавление ее как функции, рабьотающей
только с открытыми методами класса-предпочтительние.
С другой стороны, на основании материалов обсуждения: http://www.rsdn.ru/Forum/?mid=54623
Автор: jazzer
Дата: 15.05.02
,
можно сделать вывод, что любые, внешние по отношению к классу, функции,
можно переделать в функции специального дочернего классса
(мои комплименты jazzer ).


Чесно говоря, мне трудно представить ситуацию, при котором подобная
функция должна иметь доступ не только к public, но и protected членам
базового класса.
Но даже в этом случае "Шаманство" нужно не очень сложное:
class foo 
{
protected:
    int ProtectedData;
private:
    int PrivateData;
public:
    int PublicData;

    inline foo(void)
        :    ProtectedData(1) ,PrivateData(2), PublicData(3)
    {};
    inline foo(const foo&ref)
        :    ProtectedData(ref.ProtectedData) ,
            PrivateData(ref.PrivateData), 
            PublicData(ref.PublicData)
    {};
    inline foo& operator=(const foo&ref)
    {
        ProtectedData=ref.ProtectedData;
        PrivateData=ref.PrivateData;
        PublicData=ref.PublicData;
        return *this;
    };
};

void IncProtected(foo& data)
{
    class AccessToProtected : public foo
    {
    public:
        inline void IncProtected(void)
        {
            ProtectedData++;
        };
    };
    reinterpret_cast<AccessToProtected&>(data).IncProtected(); 
};


или без шаманства, если базовый класс поддерживает оператор присваивания
void IncProtected2(foo& data)
{
    class AccessToProtected : public foo
    {
    public:
        inline AccessToProtected (const foo& ref)
            : foo(ref)
        {};

        inline void IncProtected(void)
        {
            ProtectedData++;
        };
    };
    AccessToProtected A(data);
    A.IncProtected();
    data=A;
};


Если же, требуемый дополнительный метод должен обращатьться к private
методам базового класса, то вероятно имеет место ошибка при проектировании.
Даже необходимость обращения к protected данным кажется подозрительной.
Re[4]: Расширение С++
От: IAZ http://iaz.simb.ru
Дата: 24.03.03 15:41
Оценка: 18 (4)
Здравствуйте, Denwer, Вы писали:

D>Здравствуйте, Аноним, Вы писали:


D>

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

А>>Denwer, я правильно понял?


А>>--

А>>Дмитрий

D>ДА ДА ДА


Хороший способ расширения библиотеки классов — это использование оберток.
Например, пусть у нас есть библиотека классов:


class A
{
public:
   void f();
};
class B : public A
{
public:
   void g();
};



и ее требуется расширить одной фукцией void h()


template<class T>
class X
{
public:
   X(T* _a) : a(_a) {}
   void h();
   T* operator->()
   {
     return a;
   }
private:
   T* a;
};


Использовать такой класс-обертку не сложнее чем обычный класс:

X<A> xa(new A);
xa.h();  // вызов X::h()
xa->f(); // вызов A::f()

X<B> xb(new B);
xb.h();  // вызов X::h()
xb->f(); // вызов A::f()
xb->g(); // вызов B::g()



Конечно в этом случае надо знать, что к методам класса X надо обращаться через '.' а к другим через '->' Но неужели это проблема?!

Доступа к закрытым методам классов A & B тоже не получишь.
Зато дешево, надежно и практично.
Кто ищет то всегда найдет!
Re: Расширение С++
От: WolfHound  
Дата: 24.03.03 16:57
Оценка: 9 (3)
Здравствуйте, Denwer, Вы писали:

Просто библиотеки нужно писать правильно.


Классическая иерархия.
class A{};
class B:public A{};
class C:public B{};
class D:public C{};



Модерновая иерархия.
template <class T>
class Empty{};

class A{};

template <template<class>class T=Empty>
class B:public T<A>{};

template <template<class>class T=Empty>
class C:public B<T>{};

template <template<class>class T=Empty>
class D:public C<T>{};

Теперь если нам нужно расширить функциональность то пишем так
template<class Base>
class E:public Base
{};
typedef D<E> D2;


А если нужно таскать за указатель на промежуточный класс то для этого класса придется сотворить интерфейс. ИМХО не большая плата за большую гипкость.
Хотя если в своем коде то не обязательно. Но лучше завести чтобы небыло мучетельно больно.

Итого:
template <class T>
class Empty{};

class IA{};
class A:public IA{};

class IB{};
template <template<class>class T=Empty>
class B:public T<A>, public IB{};

class IC{};
template <template<class>class T=Empty>
class C:public B<T>, public IC{};

class ID{};
template <template<class>class T=Empty>
class D:public C<T>, public ID{};

class IE{};
template<class Base>
class E:public Base, public IE
{};

int main()
{
    //Можно так
    D<E> de;
    B<E>* be=&de;
    //Можно приводить к интерфейсу...
    //Можно еще что нибудь придумать...
}


ЗЫ Не забываем про виртуальные деструкторы(опущены для краткости).
... << RSDN@Home 1.0 beta 5 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: Расширение С++
От: WolfHound  
Дата: 24.03.03 18:07
Оценка:
Здравствуйте, WolfHound, Вы писали:

Опс бага вышла. Так правильно.
template <class Base>
class Empty:public Base{};
... << RSDN@Home 1.0 beta 5 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: Расширение С++
От: Denwer Россия  
Дата: 25.03.03 06:34
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, Denwer, Вы писали:


WH>Просто библиотеки нужно писать правильно.


Ага, ты мне предлагаешь написать в майкрософт и высказать свое недоверие тем, как они спроектировали MFC. Речь и идет как раз не о моих классах(в своей либе я могу делать что хочу), а о классах сторонних разработчиков, которые я править не могу(по разным причинам, например отсутствие исходников).
Re[5]: Расширение С++
От: Аноним  
Дата: 25.03.03 13:26
Оценка: 11 (3)
Здравствуйте, IAZ, Вы писали:

IAZ>Здравствуйте, Denwer, Вы писали:


D>>Здравствуйте, Аноним, Вы писали:


D>>

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

А>>>Denwer, я правильно понял?


А>>>--

А>>>Дмитрий

D>>ДА ДА ДА


IAZ>Хороший способ расширения библиотеки классов — это использование оберток.


Хороший! Тем не менее, он не всегда срабатывает. Например, если нужно добавить не просто методы, а и члены-данные.

Думаю, что не мешает ввести немного конкретики. Допустим у нас есть класс Figure с методом move(int, int) и классы Square и Circle, которые наследуются от Figure и реализуют move(int, int) по-своему.

class Figure {
private:
   // . . .
public:
   virtual void move(int dx, int dy) =0;
   // . . .
};

class Square: public Figure {
private:
    int left, top, right, bottom;
    // . . .
public:
    virtual void move(int dx, int dy);
    // . . .
};

class Circle: public Figure {
private:
    int cx, cy, radius;
    // . . .
public:
    virtual void move(int dx, int dy);
    // . . .
};


А теперь мы хотим отслеживать перемещения фигур. Скажем, когда какая-нибудь фигура перемещается она посылает сообщения своему подписчику (или подписчикам). Попробуем организовать подписку таким образом:
class MovingSubscriber {
public:
    virtual void beforeMoving(int dx, int dy) =0;
    virtual void afterMoving(int dx, int dy) =0;
};

class FigureWithSubscription: public Figure {
private:
    MovingSubscriber *subscriber;
public:
    virtual void realMove(int dx, int dy) =0;
    virtual void move(int dx, int dy) {
        if(subscriber) subscriber->beforeMoving(dx, dy);
        realMove(dx, dy);
        if(subscriber) subscriber->afterMoving(dx, dy);
    }
    virtual void setSubscriber(MovingSubscriber *s) {
        subscriber = s;
    }
};


Однако, Square и Circle ничего не знают о FigureWithSubscription и поэтому никаких сообщений при своем перемещении никому не посылают. Попробуем сделать так:

template<class T>
class GenericFigureWithSubscription: public T {
private:
    MovingSubscriber *subscriber;
public:
    virtual void move(int dx, int dy) {
        if(subscriber) subscriber->beforeMoving(dx, dy);
        T::move(dx, dy);
        if(subscriber) subscriber->afterMoving(dx, dy);
    }
    virtual void setSubscriber(MovingSubscriber *s) {
        subscriber = s;
    }
};

class SquareWithSubscription: public GenericFigureWithSubscription<Square> {
};

class CircleWithSubscription: public GenericFigureWithSubscription<Circle> {
};


Вроде все нормально. Но следует заметить, что количество дополнительного кода прямо пропорционально количеству прямых наследников от Figure, что не есть хорошо. Но и это еще не все! Данные изменения касаются только объектов, созданных разработчиком, который знает о подписке и знает, что теперь нужно создавать объекты класса SquareWithSubscription а не просто Square. Т.е. подписаться на премещения объектов, которые были созданы где-то в недрах библиотеки невозможно. Для того, чтобы сделать это реальностью, придется делать класс-оболочку, к которому можно будет подписаться и который будет делегировать вызовы реальному объекту.

class WrapperWithSubscription: public Figure {
private:
    Figure *figure;
    MovingSubscriber *subscriber;
public:
    WrapperWithSubscription(Figure *f): figure(f) {}
    virtual void move(int dx, int dy) {
        if(subscriber) subscriber->beforeMoving(dx, dy);
        figure->move(dx, dy);
        if(subscriber) subscriber->afterMoving(dx, dy);
    }
    virtual void setSubscriber(MovingSubscriber *s) {
        subscriber = s;
    }
}


А после этого следить, чтобы все двигали фигуры только через врапперы! Как, оказывается, легко тривиальную задачу превратить в большую неприятность!

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

Впрочем, чтобы немного упростить себе жизнь, придумали аспектно-ориентированное программирование. В частности, задача с перемещениями фигур могла бы решаться при помощи такого аспекта (здесь приведен псевдокод):
aspect FigureSubscrition {
    // добавление в класс Figure член-данное subscriber
    MovingSubscriber *Figure::subscriber;
    // добавление в класс Figure метод setSubscriber
    virtual void Figure::setSubscriber(MovingSubscriber *s) {
        subscriber = s;
    }

    // определение кода, который будет вызываться до и после вызова метода.
    before(Figure *p, int dx, int dy): target(p) && call(void Figure::move(int dx, int dy)) {
        if(p->subscriber) p->subscriber->beforeMoving(dx, dy);
    }
    after(Figure *p, int dx, int dy): target(p) && call(void Figure::move(int dx, int dy)) {
        if(p->subscriber) p->subscriber->afterMoving(dx, dy);
    }
}
Re[6]: Расширение С++
От: Аноним  
Дата: 25.03.03 13:31
Оценка:
Впрочем, чтобы немного упростить себе жизнь, придумали аспектно-ориентированное программирование. В частности, задача с перемещениями фигур могла бы решаться при помощи такого аспекта (здесь приведен псевдокод):

. . .

Но это все уже выходит за пределы тематики C++.

--
Дмитрий
Re[6]: Расширение С++
От: IAZ http://iaz.simb.ru
Дата: 25.03.03 20:04
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

IAZ>>Хороший способ расширения библиотеки классов — это использование оберток.


А>Хороший! Тем не менее, он не всегда срабатывает. Например, если нужно добавить не просто методы, а и члены-данные.


Почему? Кто запрещает поместить вместе с методом и атрибуты?


template<class T>
class X
{
public:
   X(T* _a) : a(_a) {}
   void h(); // метод
   int k;    // атрибут
   T* operator->()
   {
     return a;
   }
private:
   T* a;
};




А>Думаю, что не мешает ввести немного конкретики. Допустим у нас есть класс Figure с методом move(int, int) и классы Square и Circle, которые наследуются от Figure и реализуют move(int, int) по-своему.

<skip>
А>Вроде все нормально. Но следует заметить, что количество дополнительного кода прямо пропорционально количеству прямых наследников от Figure, что не есть хорошо. Но и это еще не все! Данные изменения касаются только объектов, созданных разработчиком, который знает о подписке и знает, что теперь нужно создавать объекты класса SquareWithSubscription а не просто Square. Т.е. подписаться на премещения объектов, которые были созданы где-то в недрах библиотеки невозможно.

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


А>А после этого следить, чтобы все двигали фигуры только через врапперы!


За кем следить? Подробнее см. выше.

А>Получается, что расширить библиотеку таким образом, каким явно не предусмотрел разработчик этой библиотеки, зачастую крайне трудно, если и вообще возможно.


Вот это действительно так. А может библиотека специально спроектирована не расширяемой!?

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


За каждым упрощением скрывается море проблем.

А>В частности, задача с перемещениями фигур могла бы решаться при помощи такого аспекта (здесь приведен псевдокод):


С теоретической точки зрения возможно это иногда нужно. Но только при обязательном условии: чтобы можно было это оключать или недопускать распространения на определенные объекты!
Кто ищет то всегда найдет!
Re[6]: Расширение С++
От: Артур Россия  
Дата: 26.03.03 13:07
Оценка: 2 (1)
Ну предположим ты добавляешь члены-данные, а где-то в глубине MFC код создаёт объект-наследник от CWnd и не знает, что ты добавил новый — член данных, и у CWnd изменился размер, и что тогда?

Кстати похожая функциональность есть если не ошибаюсь в JavaScript. Там можно во время выполнения добавлять в классы поля. Но это же интерпретируемый язык. В С++ такое сделать будет нелегко.
... << RSDN@Home 1.0 beta 6a >>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.