Warning C4250
От: Аноним  
Дата: 03.07.03 20:06
Оценка:
Неприятный Warning:
f:\!CPP\NewModel\test1.h(21): warning C4250: 'B' : inherits 'A::A::test1' via dominance
Можно конечно его выключить, но может в этом есть что-то неправильное? Каково ваше мнение?


struct IA
{
    virtual void test1()=0;
};
struct IB:virtual public IA
{
    virtual void test2()=0;
};
class A:virtual public IA
{
public:
    virtual void test1(){cout<<"test1()"<<endl;}
};

class B:public A,virtual public IB
{
public:
    virtual void test2(){cout<<"test2()"<<endl;}
};
Re: Warning C4250
От: Anton V. Kolotaev  
Дата: 04.07.03 07:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Неприятный Warning:

А>f:\!CPP\NewModel\test1.h(21): warning C4250: 'B' : inherits 'A::A::test1' via dominance
А>Можно конечно его выключить, но может в этом есть что-то неправильное? Каково ваше мнение?

Comeau не ругнулся.
Re: Warning C4250
От: promko Украина  
Дата: 04.07.03 07:47
Оценка:
A> Можно конечно его выключить,
это warning 2-го уровня
A>но может в этом есть что-то неправильное? Каково ваше мнение?
This warning is for informational purposes only.
Posted via RSDN NNTP Server 1.6
Re[2]: Warning C4250
От: astral_marine  
Дата: 23.01.08 16:10
Оценка:
Здравствуйте, promko, Вы писали:

P>это warning 2-го уровня

P>This warning is for informational purposes only.

В MSDN сказано немного иначе:

Level 0 disables all warnings.
Level 1 displays severe warnings. Level 1 is the default warning level at the command line.
Level 2 displays all level 1 warnings and warnings less severe than level 1.
Level 3 displays all level 2 warnings and all other warnings recommended for production purposes.
Level 4 displays all level 3 warnings plus informational warnings, which in most cases can be safely ignored. This option should be used only to provide "lint" level warnings and is not recommended as your usual warning level setting.

MSDN: /w, /Wn, /WX, /Wall, /wln, /wdn, /wen, /won (Warning Level)
MSDN: Compiler Warning (level 2) C4250

По умолчанию в компиляторе установлен 3-й уровень, а 2-й уровень это более серъезные предупреждения.
Компилятор Comeau действительно не видит в таком наследновании ничего плохого.

Можно ли сделать рефакторинг этого кода так, что бы это предупреждение не возникало и при этом не отключать само предупрежедение. Подозреваю что у Visual C++ с этим кодом могут быть проблемы, раз такой высокий уровень самого предупреждения.
Re[3]: Warning C4250
От: Left2 Украина  
Дата: 23.01.08 16:30
Оценка: +1 -1
_>Можно ли сделать рефакторинг этого кода так, что бы это предупреждение не возникало и при этом не отключать само предупрежедение. Подозреваю что у Visual C++ с этим кодом могут быть проблемы, раз такой высокий уровень самого предупреждения.

Правильно!
Нужно выкинуть из дизайна виртуальное наследование. Ибо оно от лукавого.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[4]: Warning C4250
От: astral_marine  
Дата: 24.01.08 08:19
Оценка:
Здравствуйте, Left2, Вы писали:
L>Нужно выкинуть из дизайна виртуальное наследование. Ибо оно от лукавого.

Полностью согласен!

Вопрос в том как это сделать не спихивая весь код в один большой жирный класс.
Re[5]: Warning C4250
От: Left2 Украина  
Дата: 24.01.08 08:48
Оценка:
_>Вопрос в том как это сделать не спихивая весь код в один большой жирный класс.

Запросто.
Посмотреть, к примеру, как это сделано в ATL.
Там класс может реализовывать много разных интерфейсов, унаследованных от IUnknown, при этом реализация IUnknown всегда одна. Грубо говоря, схема такая:

CComObjectRoot <- CSomeRealClass <- CComObject<CSomeRealClass>

CComObjectRoot реализует функциональность IUnknown "по умолчанию": InternalAddRef, InternalRelease, InternalQueryInterface
CSomeRealClass (унаследован от CComObjectRoot) может перекрывать эту функциональность, к примеру он даёт выставить дополнительные интерфейсы в InternalQueryInterface
CComObject<CSomeRealClass> (унаследован от CSomeRealClass) предоставляет реальную реализацию IUnknown — AddRef дёргает InternalAddRef, Release дёргает InternalRelease, и т.п.

Из всех этих трёх классов реально создаются обьекты только класса CComObject<CSomeRealClass>. При этом класс CComObjectRoot и шаблон CComObject — библиотечные, пишется пользователем только CSomeRealClass.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[6]: Warning C4250
От: astral_marine  
Дата: 24.01.08 09:42
Оценка:
Подход, реализованный в ATL, очень интересен.
Класс можно сделать шаблонным, и в качестве параметра шаблона будет выступать базовый интерфейс для конкретного класса:
#include <iostream>

struct IA
{
    virtual void test1() = 0;
};
struct IB : public IA
{
    virtual void test2() = 0;
};

template <class TBaseClass>
class ARoot : public TBaseClass
{
public:
    virtual void test1() { std::cout << "test1()" << std::endl; }
};

class A : public ARoot<IA>
{

};

class B : public ARoot<IB>
{
public:
    virtual void test2() { std::cout << "test2()" << std::endl; }
};

Но при это возникает другая проблема: реализация метода ARoot::test1() на самом деле это куча методов которые в теле используют кучу других классов и доступность последних классов в области видимости класса B крайне нежелательна.

Другими словами реализацию метода A::test1() нежелательно делать шаблонной, он должен быть реализован так, будто знает только об интерфейсе IA, а об интерфейсе IB он в принципе знать и не должен, а моя реализация в примере ARoot::test1() эти знания допускает.
Re[7]: Warning C4250
От: Left2 Украина  
Дата: 24.01.08 10:11
Оценка:
#include <iostream>

struct IA
{
    virtual void test1() = 0;
};
struct IB : public IA
{
    virtual void test2() = 0;
};

template <class TBaseClass, class TRealClass>
class ARoot : public TBaseClass
{
public:
    virtual void test1() { static_cast<TRealClass*>(this)->do_test1(); }

    void do_test1() { std::cout << "default implementation" << std::endl; }
};

class A : public ARoot<IA, A>
{
    void do_test1() { std::cout << "A::test1()" << std::endl; }
};

class B : public ARoot<IB, B>
{
public:
    void do_test1() { std::cout << "B::test1()" << std::endl; }
    
    virtual void test2() { std::cout << "test2()" << std::endl; }
};
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[8]: Warning C4250
От: Left2 Украина  
Дата: 24.01.08 10:20
Оценка:
На самом деле если вернуться к изначальному примеру (CComObject(Root)) то там сделано чуть по-другому

#include <iostream>

struct IA
{
    virtual void test1() = 0;
};
struct IB : public IA
{
    virtual void test2() = 0;
};

class ARoot : public TBaseClass
{
public:
    void do_test1() { std::cout << "default implementation" << std::endl; }
};

template<class T>
class AFinish : public T
{
  virtual void test1() { return do_test1(); }
}

class A : public ARoot
{
    void do_test1() { std::cout << "A::test1()" << std::endl; }
};

class B : public ARoot
{
public:
    void do_test1() { std::cout << "B::test1()" << std::endl; }
    
    virtual void test2() { std::cout << "test2()" << std::endl; }
};

// И потом классы инстанцируются вот так:
AFinish<A> a;
AFinish<B> b;
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[9]: Warning C4250
От: Left2 Украина  
Дата: 24.01.08 10:23
Оценка:
Поправил синтаксические ошибки:

#include <iostream>

struct IA
{
    virtual void test1() = 0;
};
struct IB : public IA
{
    virtual void test2() = 0;
};

class ARoot : public IA
{
public:
    void do_test1() { std::cout << "default implementation" << std::endl; }
};

template<class T>
class AFinish : public T
{
  virtual void test1() { return do_test1(); }
}

class A : public ARoot
{
    void do_test1() { std::cout << "A::test1()" << std::endl; }
};

class B : public ARoot, IB
{
public:
    void do_test1() { std::cout << "B::test1()" << std::endl; }
    
    virtual void test2() { std::cout << "test2()" << std::endl; }
};

// И потом классы инстанцируются вот так:
AFinish<A> a;
AFinish<B> b;
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[10]: Warning C4250
От: astral_marine  
Дата: 24.01.08 11:27
Оценка:
Еще поправил синтаксические ошибки:
struct IA
{
    virtual void test1() = 0;
};
struct IB : public IA
{
    virtual void test2() = 0;
};

class ARoot : public IA
{
public:
    void do_test1() { std::cout << "default implementation" << std::endl; }
};

template<class T>
class AFinish : public T
{
public:
    virtual void test1() { return do_test1(); }
};

class A : public ARoot
{
public:
    void do_test1() { std::cout << "A::test1()" << std::endl; }
};

class B : public ARoot, IB
{
public:
    void do_test1() { std::cout << "B::test1()" << std::endl; }

    virtual void test2() { std::cout << "test2()" << std::endl; }
};

AFinish<A> a;
a.test1();
AFinish<B> b;
b.test1();
b.test2();


Но здесь же опять используется множественное наследование, и, что хуже, класс B будет иметь два интерфейса IA, "ромбика" наследования здесь не будет поскольку не используется виртуальное наследование.
Также передполагалось, что класс B будет использовать реализацию метода A::test1(), а здесь это не так.

Или я ошибаюсь?
Re[11]: Warning C4250
От: Left2 Украина  
Дата: 24.01.08 11:34
Оценка:
_>Но здесь же опять используется множественное наследование,
Ну и прекрасно, против множественного наследования лично я ничего не имею.

_> и, что хуже, класс B будет иметь два интерфейса IA,

Не совсем так. Данных в интерфейсе IA нет, а его реализации одинаковы (вызываются те же самые виртуальные функции) в обеих цепочках наследования.
Если же для реализации интерфейса IA нужны какие-то данные — их можно добавить в ARoot.

_>"ромбика" наследования здесь не будет поскольку не используется виртуальное наследование.

И слава Богу Против него вроде как и боролись.

_>Также передполагалось, что класс B будет использовать реализацию метода A::test1(), а здесь это не так.

Ну так убери его функцию do_test1 — и он тут же начнёт использовать реализацию из класса A.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[12]: Warning C4250
От: astral_marine  
Дата: 24.01.08 13:40
Оценка:
Здравствуйте, Left2, Вы писали:
_>>Также передполагалось, что класс B будет использовать реализацию метода A::test1(), а здесь это не так.
L>Ну так убери его функцию do_test1 — и он тут же начнёт использовать реализацию из класса A.

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

////////// A //////////

struct IA
{
    virtual void test1() = 0;
};

class A : public IA
{
public:
    void do_test1() { std::cout << "A::test1()" << std::endl; }
};

template<class T>
class AFinish : public T
{
public:
    virtual void test1() { return do_test1(); }
};

////////// B //////////

struct IB : public IA
{
    virtual void test2() = 0;
};

class B : public A, public IB
{
public:
    void do_test1() { std::cout << "B::test1()" << std::endl; }
    void do_test2() { std::cout << "B::test2()" << std::endl; }
};

template<class T>
class BFinish : public AFinish<T>
{
public:
    virtual void test2() { return do_test2(); }
};

////////// Tests //////////

AFinish<A> a;
IA & ia = a;
ia.test1();

BFinish<B> b;
IB & ib = b;
ib.test1();
ib.test2();


В принципе все подходит, но есть побочный эффект: на каждый интерфейс в экземпляре класса создается свой указатель на таблицу виртуальных функций. В данно случае у класса B их два.
Если есть много маленьких объектов с длинной иерархией наследования интерфейсов, то можно получить существенный рост занимаемой памяти.
Может быть есть способ хранить в данном случае один указатель на таблицу виртуальных функций, а не несколько?
Re[13]: Warning C4250
От: Left2 Украина  
Дата: 24.01.08 14:26
Оценка: 6 (1)
_>Похоже, что идея эта работает,
Ещё бы, на ней всё-таки ATL построен.

_>у меня получилось следующее после редактирования:

Здесь ты извратил главную идею. Root/Finish классы создаются только для "общего предка" — в нашем случае IA

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

_>Если есть много маленьких объектов с длинной иерархией наследования интерфейсов, то можно получить существенный рост занимаемой памяти.
Отдельный vtbl нужен только для "листовых" классов. Вот посмотри, я чуть усложнил пример:
#include <iostream>

struct IA
{
    virtual void test1() = 0;
};

struct IB : public IA
{
    virtual void test2() = 0;
};

struct IC : public IB
{
    virtual void test3() = 0;
};

class ARoot
{
public:
    void do_test1() { std::cout << "default implementation" << std::endl; }
};

template<class T>
class AFinish : public T
{
public:
    virtual void test1() { return do_test1(); }
};

template<class TI, class TReal>
class BImpl : public TI
{
public:
    virtual void test2() { return static_cast<TReal*>(this)->do_test2(); }

    void do_test2() { std::cout << "default implementation" << std::endl; }
};

class C : 
    public ARoot, 
    public BImpl<IC, C>
{
public:
    void do_test1() { std::cout << "B::test1()" << std::endl; }
    void do_test2() { std::cout << "B::test2()" << std::endl; }

    virtual void test3() { std::cout << "B::test3()" << std::endl; }
};


int main()
{
    AFinish<C> b;

    b.test1();
    b.test2();
    b.test3();
}

Здесь у обьекта b только один vtbl. Правда, если бы был ещё один интерфейс ID, унаследованный от IB и его тоже нужно было бы поддержать — то появился бы второй vtbl, тут никуда не денешься.
... << RSDN@Home 1.2.0 alpha rev. 717>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.