Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > включение вложенных определений посредством includ


Автор: J0ker 6.5.2009, 22:00
возник такой вопрос
допустим имеется определение класса в который вложены определения других классов
естественно все это трудно читаемо
кошерно-ли вынести оперделения вложенных классов в отдельные файлы, а затем эти файлы вставить посредством #include?
пример:

a.def
Код

class A
{
};


b.def
Код

class B
{
};


c.h
Код

class C
{
private:
    #include "a.def"
public:
    #include "b.def"
};


Автор: Lazin 6.5.2009, 22:31
а что мешает сделать так:

файл detail/C.h
Код

namespace detail
{
  class A {...};
  class B {...};
}


файл С.h
Код


#include <detail/C.h>

class C
{
public:
typedef detail::A A;
typedef detail::B B;
};

Автор: J0ker 6.5.2009, 22:49
Lazin, хорошее решение, однако не всегда применимое:
Код

class B
{
    class A
    {
        B *b;
        void foo()
        {
            b->i;
        }
    };
private:
    int i;
};


...хотя можно и зафрендить...
но думаю найдутся и другие недостатки

Автор: azesmcar 6.5.2009, 23:11
Код

//header_a_h

class A
{
private:
    class B;
};


Код

//header_b_h

#include "a.h"

class A::B
{
};

Так не пойдет?

Добавлено через 6 минут и 16 секунд
J0ker

Насчет варианта с индклудами - это на мой взгляд не кашерно, ибо обявление класса дублируется и несмотря на то что он обявлен в private секции методом инклуда, его обявлении прекрасно будет фигурировать в глобальном пространстве имен нарушая всю вашу инкапсуляцию smile 

Автор: mes 6.5.2009, 23:39
Цитата(J0ker @  6.5.2009,  21:00 Найти цитируемый пост)
кошерно-ли вынести оперделения вложенных классов в отдельные файлы, а затем эти файлы вставить посредством #include?

думаю не стоит так делать.

мне кажетеся Вам подойдет такой вариант : 

Код

// c.h
class C
{
  class A;
  class B;
//...
private:
   A  * p_a;
   B  * p_b;
};


Код

// c_a.h
#include "c.h"
class C::A
{
};


Код

// c_b.h
#include "c.h"
class C::B
{
};


Код

// c.cpp
#include "c.h"
#include "c_a.h"
#include "c_b.h"

C::C() { p_a = new A(); p_b=new B(); }


Оп...оказывается я опоздал с предложением ;)


Автор: J0ker 7.5.2009, 01:23
Цитата(azesmcar @  6.5.2009,  23:11 Найти цитируемый пост)
Так не пойдет?

Цитата(mes @  6.5.2009,  23:39 Найти цитируемый пост)
мне кажетеся Вам подойдет такой вариант :

да, похоже это то, что нужно
было-бы еще интересно узнать мнение UnrealMan'а - эквивалентно ли это семантически прямому включению

Цитата(azesmcar @  6.5.2009,  23:11 Найти цитируемый пост)
Насчет варианта с индклудами - это на мой взгляд не кашерно, ибо обявление класса дублируется 

это каким таким образом?

Цитата(azesmcar @  6.5.2009,  23:11 Найти цитируемый пост)
и несмотря на то что он обявлен в private секции методом инклуда, его обявлении прекрасно будет фигурировать в глобальном пространстве имен нарушая всю вашу инкапсуляцию 

каким образом оно попадет в глобальное пространство?

Автор: mes 7.5.2009, 01:40
Цитата(J0ker @  7.5.2009,  00:23 Найти цитируемый пост)
эквивалентно ли это семантически прямому включению

В данном вопросе Вы подразумеваете, будут ли на классы описанные вышеупомянутым способом, распространяться те же правила, как если бы он был описан внутри класса-хозяина ?

если да, то 
Цитата

  9.7  Nested class declarations                            [class.nest]

1 A class can be defined within another class.  A class  defined  within
  another is called a nested class.  The name of a nested class is local
  to its enclosing class.  The nested class  is  in  the  scope  of  its
  enclosing  class.   Except by using explicit pointers, references, and
  object names, declarations in a nested class can use only type  names,
  static members, and enumerators from the enclosing class.  [Example:
          int x;
          int y;

          class enclose {
          public:
              int x;
              static int s;
              class inner {
                  void f(int i)
                  {
                      int a = sizeof(x); // error: refers to enclose::x
                      x = i;   // error: assign to enclose::x
                      s = i;   // ok: assign to enclose::s
                      ::x = i; // ok: assign to global x
                      y = i;       // ok: assign to global y
                  }
                  void g(enclose* p, int i)
                  {
                      p->x = i;   // ok: assign to enclose::x
                  }
              };
          };

          inner* p = 0;   // error `inner' not in scope
   --end example]

2 Member  functions  and  static  data  members of a nested class can be
  defined in a namespace scope enclosing the definition of their  class.
  [Example:

          class enclose {
          public:
              class inner {
                  static int x;
                  void f(int i);
              };
          };
          int enclose::inner::x = 1;

          void enclose::inner::f(int i) { /* ... */ }
   --end example]

3 If  class  X  is defined in a namespace scope, a nested class Y may be
  declared in class X and later defined in the definition of class X  or
  be  later  defined  in  a  namespace scope enclosing the definition of
  class X.  [Example:
          class E {
              class I1;      // forward declaration of nested class
              class I2;
              class I1 {};  // definition of nested class
          };
          class E::I2 {};   // definition of nested class
   --end example]

4 Like a member function, a  friend  function  (_class.friend_)  defined
  within  a nested class is in the lexical scope of that class; it obeys
  the same rules for name binding as a static member  function  of  that
  class  (_class.static_) and has no special access rights to members of
  an enclosing class.


 smile 

Автор: J0ker 7.5.2009, 06:27
mes, спасибо было лень самому копать  smile 

Автор: azesmcar 7.5.2009, 07:04
Цитата(J0ker @  7.5.2009,  01:23 Найти цитируемый пост)
каким образом оно попадет в глобальное пространство? 

методом прямого инклуда файла a.def smile
не знаю можно ли это считать нарушением инкапсуляции, на самом деле если человеку не терпится навредить себе, он это сделает..ведь
Код

#define private public

еще не отменяли smile 
но в любом случае, вышеупомянутый вариант - лучше

Автор: J0ker 7.5.2009, 08:51
Цитата(azesmcar @  7.5.2009,  07:04 Найти цитируемый пост)
методом прямого инклуда файла a.def

инклюд просто включает текст из одного файла в другой
если я делаю инклюд в скопе класса, то все, что в инклюде написано попадает в скоп класса и никаким боком наружу
впрочем вопрос решен уже

Автор: azesmcar 7.5.2009, 09:00
Цитата

инклюд просто включает текст из одного файла в другой
если я делаю инклюд в скопе класса, то все, что в инклюде написано попадает в скоп класса и никаким боком наружу
впрочем вопрос решен уже 

А я что-то другое сказал?
Код

class A
{
private:
   #include "b.def"
}

теперь если я - пользователь класса сделаю у себя в начале файла
#include "b.def"
класс В попадет в мое глобальное пространство имен, и он не будет защищен секцией private. Я об этом.
т.е. мы даем пользователю возможность включить этот файл у себя. Ничего особо страшного, но лучше этого не позволять.

Автор: mes 7.5.2009, 09:05
Цитата(azesmcar @  7.5.2009,  08:00 Найти цитируемый пост)
теперь если я - пользователь класса сделаю у себя в начале файла
#include "b.def"
класс В попадет в мое глобальное пространство имен, и он не будет защищен секцией private. Я об этом.


а кто разрешает пользователю инклудить "не инклудные" файлы ?! 
J0ker же специально обозвал его *.def  smile 
В таком случае однозначно ответственность на юзере. 
Также как если вдруг ему захочется заинклудить *cpp файл (хотя бывают случаи и с таким применением smile )

Автор: azesmcar 7.5.2009, 09:10
Цитата

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

ну, с большей долей вероятности при включении *cpp файла он получит ошибку времени линковки.
Цитата

а кто разрешает пользователю инклудить "не инклудные" файлы ?! 
J0ker же специально обозвал его *.def  smile 
В таком случае однозначно ответственность на юзере. 

 smile 
я только говорю что - зачем давать пользователю лишний шанс нарушить инкапсуляцию, если есть другие методы.

Автор: mes 7.5.2009, 09:33
Цитата(azesmcar @  7.5.2009,  08:10 Найти цитируемый пост)
я только говорю что - зачем давать пользователю лишний шанс нарушить инкапсуляцию, если есть другие методы.

от не сознательных действий юзера можно было бы, например, поставить защиту :
Код

class C
#define  MY_KEY_FOR_INCLUDE_B_DEF_H_
#include "b.def"
#undef  MY_KEY_FOR_INCLUDE_B_DEF_H_

//b.def
#ifdef MY_KEY_FOR_INCLUDE_B_DEF_H_
..
#endif 

но, имхо, дело не в пользователe, а в самой постановке кода. Если есть более чистый вариант, зачем прибегать к трюкам ?! 

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


Цитата(azesmcar @  7.5.2009,  08:10 Найти цитируемый пост)
ну, с большей долей вероятности при включении *.cpp файла он получит ошибку времени линковки.

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

Автор: azesmcar 7.5.2009, 09:55
Цитата(mes @  7.5.2009,  09:33 Найти цитируемый пост)
но, имхо, дело не в пользователe, а в самой постановке кода. Если есть более чистый вариант, зачем прибегать к трюкам ?! 

ну я и говорю
Цитата(azesmcar @  7.5.2009,  09:10 Найти цитируемый пост)
если есть другие методы.


Цитата(mes @  7.5.2009,  09:33 Найти цитируемый пост)

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

это те cpp шники которые специально для этого разработаны, я говорил не о них а об обычных.

Автор: mes 7.5.2009, 10:03
Цитата(azesmcar @  7.5.2009,  08:55 Найти цитируемый пост)
это те cpp шники которые специально для этого разработаны, я говорил не о них а об обычных.

ну так а b.def разве не специально разработан ?!  smile   smile 


Цитата(azesmcar @  7.5.2009,  08:55 Найти цитируемый пост)
ну я и говорю

я лишь пытался уточнить, потому как чуствовалась двойная трактовка, но в принципе, да, речь об одном и том же  smile 

Автор: azesmcar 7.5.2009, 10:06
Цитата

ну так а b.def разве не специально разработан ?! 

специально, только вот если включить те файлы шаблонных функций - то ничего не нарушится, они специально предназначены для включения в глобальное пространство имен, а наш b.def предназначен для включения в приватную секцию (хотя никто не зарпещает включить его глобально).  smile 
В принципе это параноя если смотреть с практической точки зрения smile
Но если взглянуть с теоретической точки зрения, то есть более рациональный аналог. Только и всего smile

Автор: J0ker 7.5.2009, 16:33
Цитата(azesmcar @  7.5.2009,  10:06 Найти цитируемый пост)
специально, только вот если включить те файлы шаблонных функций - то ничего не нарушится, они специально предназначены для включения в глобальное пространство имен, а наш b.def предназначен для включения в приватную секцию (хотя никто не зарпещает включить его глобально).

boost/mem_fn.hpp
Код

......
namespace _mfi // mem_fun_impl
{

template<class V> struct mf
{

#define BOOST_MEM_FN_RETURN return

#define BOOST_MEM_FN_NAME(X) inner_##X
#define BOOST_MEM_FN_CC

#include <boost/bind/mem_fn_template.hpp>

#undef BOOST_MEM_FN_CC
#undef BOOST_MEM_FN_NAME
.........

boost/bind/mem_fn_template.hpp
Код

//
//  bind/mem_fn_template.hpp
//
//  Do not include this header directly
//
....

Автор: azesmcar 7.5.2009, 16:35
J0ker smile 
mes сказал немного выше что так поступают в стандартных библиотеках..но только когда другого выхода нет.

Автор: J0ker 7.5.2009, 17:04
Цитата(azesmcar @  7.5.2009,  16:35 Найти цитируемый пост)
J0ker  
mes сказал немного выше что так поступают в стандартных библиотеках..но только когда другого выхода нет.


Цитата(azesmcar @  7.5.2009,  10:06 Найти цитируемый пост)
 а наш b.def предназначен для включения в приватную секцию (хотя никто не зарпещает включить его глобально).  

а наш boost/bind/mem_fn_template.hpp предназначен для включения в публичную секцию (хотя никто не зарпещает включить его глобально)

Автор: azesmcar 7.5.2009, 17:13
Цитата(J0ker @  7.5.2009,  17:04 Найти цитируемый пост)
а наш boost/bind/mem_fn_template.hpp предназначен для включения в публичную секцию (хотя никто не зарпещает включить его глобально)

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)