| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 «  : 27-02-2009 19:43 »   | 
								
								 | 
							  
							 
							Есть практическая задача, написать единый интерфейс для работы с несколькими движками баз данных. Сейчас это реализовано примерно так: class IDatabase { public:     virtual void someMethod1() = 0;     virtual void someMethod2() = 0; };
  class CDatabase1 : public  IDatabase { public:     void someMethod1() { }     void someMethod2() { } };
  class CDatabase2 : public  IDatabase { public:     void someMethod1() { }     void someMethod2() { } };
  typedef enum _DB_TYPE {      DATABASE_1,      DATABASE_2 }DB_TYPE;
  class CDatabase { private:     static IDatabase *_inst; public:     CDatabase(DB_TYPE db_type) {         if( _inst == 0 ) {             switch(db_type) {                 case DATABASE_1:                     _inst = new CDatabase1();                 break;                 case DATABASE_2:                     _inst = new CDatabase2();                 break;             }         }     }     static IDatabase *get_inst() { return _inst; }     static void free_inst() {         if( _inst != 0 ) {             delete _inst;             _inst = 0;         }     } }
 Так вот, вопрос состоит в следующем: хотя этот код позволяет достичь поставленой задачи, но приходится освобождать память в конце программы, вызывая метод: хотелось бы от этого избавиться. Возможный вариант это объявление ссылкой переменную: в Тогда память освобождалась бы автоматически. Но как инициализировать статическую ссылку нужным классом при вызове конструктора? CDatabase::CDatabase(DB_TYPE db_type)
   
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #1 : 27-02-2009 19:53 »   | 
								
								 | 
							  
							 
							Ну первое, а зачем вообше ты используеш статическую переменную? Стоит тебе в программе создать два экземпляра данного класса и память потекла струёй. И еше вопрос, знаеш ли ты, что сушествуют деструкторы, которые срабатывают автоматически при уничтожении класса. 
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #2 : 27-02-2009 19:59 »   | 
								
								 | 
							  
							 
							Finch,  1.Статическая переменная применяется т.к. нужен всего один экземпляр класса на всю программу (паттерн Синглетон)  2.Создать еще один экземпляр не получится, от этого защищает условие: if( _inst == 0 ) { // _inst - статическая и при следующем вызове конструктора она уже будет отличаться от нуля             switch(db_type) {                 case DATABASE_1:                     _inst = new CDatabase1();                 break;                 case DATABASE_2:                     _inst = new CDatabase2();                 break;             }         }
 3.Деструктор нельзя использовать потому что если я случайно создам где нибудь в середине программы экземпляр класса CDatabase, то после выхода переменной этого класса за пределы видимости будет вызван деструктор, который удалит статический экземпляр класса, указатель на который храниться в _inst  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 27-02-2009 20:03 от RuNTiME »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #3 : 27-02-2009 20:22 »   | 
								
								 | 
							  
							 
							RuNTiME, ּSingleton строится по другой схеме. И конструктор и деструктор прячутся в private зоне. Наружу вытаскивается статический Init и Done в которых и создается один единственный класс. #include <iostream> class single { public:    static single* Init();    void Done(); private:    single() {std::cout << "Constructor" << std::endl;};    ~single() {std::cout << "Destructor" << std::endl;};        static single *ehid;     };
  single * single::ehid=NULL;
  single *single::Init() {    if (!ehid) ehid = new single();    return ehid; }
  void single::Done() {    delete this;    ehid = NULL; }
  int main() {    single *exem1=single::Init();    single *exem2=single::Init();    exem1->Done();    return 0; }
  Можеш вести счетчик вызовов и при обнулении только уничтожать класс. В том примере, что ты привел, все равно возможна течь памяти.  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 27-02-2009 20:24 от Finch »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #4 : 27-02-2009 20:39 »   | 
								
								 | 
							  
							 
							Finch,  я знаю, что можно так сделать синглетон... мой пример приводиться к твоему переносом кода из конструктора в статическую функцию Init..., но проблема сейчас не в этом, мне не хочеться вызывать функцию Done вообще как таковую.... Фишка в том, что если удастся инициализировать статическую ссылку вместо указателя нужным классом, то память будет освобождена автоматом, без вызова Done... 
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #5 : 27-02-2009 20:45 »   | 
								
								 | 
							  
							 
							Finch, и кстати твой пример можно реализовать еще проще, возвращая ссылку: class CSingleton { private:     CSingleton() { } public:     static CSingleton &inst() {         static CSingleton _inst;         return _inst;      }     void someMethod() { } };
  CSingleton &c = CSingleton::inst(); c.someMethod();
 И кстати еще по твоему примеру... не стоит запирать деструктор класса в private  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #6 : 27-02-2009 20:55 »   | 
								
								 | 
							  
							 
							RuNTiME, Когда вызывается конструктор, то идет автоматическое выделение памяти на экземпляр класса. Даже если у тебя пустой конструктор. Получается, что у тебя провисает память. Кстати, если ты сделаеш даже статическую ссылку. Экземпляр класса с интерфейсом IDatabase все равно должен где то хранится. Значит будет кушать память. С другой стороны твой пример можно решить с помошью шаблонов. include <iostream> class IDatabase { public:     virtual void someMethod1() = 0;     virtual void someMethod2() = 0; };
  class CDatabase1 : public  IDatabase { public:     void someMethod1() { }     void someMethod2() { } };
  class CDatabase2 : public  IDatabase { public:     void someMethod1() { }     void someMethod2() { } };
  typedef enum _DB_TYPE {      DATABASE_1,      DATABASE_2 }DB_TYPE;
  template <typename T> class CDatabase: public T { public:     CDatabase()      {        std::cout << "Constructor" << std::endl;     }     ~CDatabase()      {        std::cout << "Destructor" << std::endl;     } };
  int main() {    CDatabase<CDatabase1> ehid;
     return 0; }
   
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #7 : 27-02-2009 20:58 »   | 
								
								 | 
							  
							 
							И кстати еще по твоему примеру... не стоит запирать деструктор класса в private
  Ну шаловливые ручки программистов еше никто не отменял. Если я например использую счетчик вызовов, то будет не очень приятно, если кто либо сдуру вызовет деструктор заместо метода Done. А так компилятор на попытку delete exem1; еше на этапе компиляции покажет язык.  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 27-02-2009 21:00 от Finch »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #8 : 27-02-2009 21:11 »   | 
								
								 | 
							  
							 
							Finch,  1.) Насчет автоматического выделения памяти в конструкторе: class CSingleton { private:     CSingleton() { } public:     static CSingleton &inst() {         static CSingleton _inst;         return _inst;      }     void someMethod() { } };
  CSingleton &c = CSingleton::inst(); c.someMethod();
 Функция inst объявлена как статическая, а это значит, что она может вызываться без создания экземпляра класса... фактически класс для этой функции является всего лишь пространством имен. Отсюда выходит следующая последовательность: 1.Вызывается inst 2.Внутри inst происходит выделение памяти под Статическую переменную _inst куда записывается 1 экземпляр класса CSingleton При дальнейших вызовах метода CSingleton::inst() выделения памяти больше происходить не будет, а это значит мы имеем 1 экземпляр класса CSingleton и никакой утечки памяти. 2.) Теперь что касается IDatabase ... по его определению видно, что это чисто виртуальный класс и переменных в этом классе нет... если ты попытаешься создать экземпляр чисто виртуального класса, то компилятор будет очень недоволен:) Под чисто виртуальные классы память не выделяется вообще, они просто служат сигналом для компилятора, что все классы наследники должны иметь в себе реализации объявленных в виртуальном классе функции. Отсюда мы НЕ имеем никакой утечки памяти на IDatabase. 3.) Насчет шаблонов, так решить можно. Но как ты себе представляешь передачу класса-шаблона в параметре функции.... это же придется указывать типы для каждой функции. Если использовать просто интерфейс то можно передавать его в параметре: void someFunction(IDatabase *db); //или void someFunction(IDatabase &db);
   
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #9 : 27-02-2009 21:21 »   | 
								
								 | 
							  
							 
							3) Посмотри как я сделал. Я наследуюсь  от класса, поэтому твоя выкладка будет также хорошо работать, и компилятор даже ничего не скажет.  2) Я специяально подчеркнул Экземпляр класса с интерфейсом IDatabase
  То что, нельзя создать экземпляр абстрактного класса я знаю. Но ты от него наследуюшся, значит можно создать дочернии классы. Дай точную формулировку задачи, а то мы сейчас ходим кругами, проверяя знания друг друга. Может твоя задача будет решаться намного проше и легче.  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 27-02-2009 21:23 от Finch »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #10 : 27-02-2009 21:32 »   | 
								
								 | 
							  
							 
							Finch,По поводу шаблонов проверю как работает.... Я специяально подчеркнул Цитировать Экземпляр класса с интерфейсом IDatabase То что, нельзя создать экземпляр абстрактного класса я знаю. Но ты от него наследуюшся, значит можно создать дочернии классы.
  Дочерние классы да можно, но не нужно    Но чтобы они не создавались, пока в голову приходит только  запереть определения служебных классов в private секцию CDatabase. Но лень потом разыменовывать:  CDatabase::CDatabase1::someFunction1() { }
  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #11 : 27-02-2009 21:34 »   | 
								
								 | 
							  
							 
							Finch,А точную задачу я сформулировал еще в самом начале    мне надо чтобы через один и тот же интерфейс можно было работать с разными объектами конкретных баз данных.... причем сделать это так, чтобы не надо было вызывать функции типа free_inst или Done    
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #12 : 27-02-2009 22:13 »   | 
								
								 | 
							  
							 
							Скажем так, что это только как ты представляеш решение. Но не сама задача. Можно например вообше не заниматься ерундой, а сделать обычный конструктор, обычную (не static) переменную указатель на экземляр класса баз данных. В конструкторе создавать базу, в деструкторе удалять. И будет счастье. Ты начинаеш переводить все в static обзывая это singleton. Не расказывая, для каких целей ты это делаеш. Поэтому наверно очень сложно найти подходяшее решение. 
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #13 : 27-02-2009 22:26 »   | 
								
								 | 
							  
							 
							Finch,  Не расказывая, для каких целей ты это делаеш.  Хорошо... рассказываю: Программа соединяется с БД один раз при старте, далее по всей программе должно использоваться это подключение, по этому static... Если делать как говоришь ты, создавать в конструкторе и удалять в деструкторе, то получается каждый раз программа будет переподключаться к БД... А это не совсем хорошо. И еще один момент: У меня существует соответсвующий класc CQuery для исполнения SQL запросов. Этот класс тоже использует созданное подключение к БД. И когда оно статическое, становиться очень удобно выполнять запросы, все сводится примерно к этому:     CQuery query(         "CREATE TABLE login("             "ID INTEGER PRIMARY KEY AUTOINCREMENT,"             "AUTH_DATA CHAR(32) UNIQUE NOT NULL,"             "REG_TIME BIGINT,"         "LAST_AUTH BIGINT);"     );     return query.exec();
  отпадает даже надобность передавать в пареметре query класс CDatabase, что весьма удобно. Что касается реализации нескольких БД.... хотелось бы научить программу работать с несколькими базами данных через один интерфейс... к примеру SqLite, MySQL, PgSQL и все через один класс.... Чтобы не переписывать код программы под API каждой конкретной СУБД.  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #14 : 27-02-2009 23:06 »   | 
								
								 | 
							  
							 
							Ну экземпляр класса нужно где то хранить, если правда ты весь класс не сделаеш static, мое предложение создавать его только один раз в main примерно так #include <iostream>
  class IDatabase { public:     virtual void someMethod1() = 0;     virtual void someMethod2() = 0; };
  class CDatabase1 : public  IDatabase { public:     void someMethod1() { }     void someMethod2() { } };
  class CDatabase2 : public  IDatabase { public:     void someMethod1() { }     void someMethod2() { } };
  template <typename T> class CDatabase: public T {    typedef CDatabase<T> Database; public:      CDatabase()      {        std::cout << "Constructor" << std::endl;        if (_inst) throw 0;        _inst = this;     }     ~CDatabase()      {        std::cout << "Destructor" << std::endl;        _inst = NULL;     }          static Database *get_inst() {return _inst;}  private:     static Database *_inst; }; template <typename T> CDatabase<T> *CDatabase<T>::_inst=NULL;
 
  int main() {    CDatabase<CDatabase1> ehid;
     return 0; }
  Заметь, повторно класс с таким же интерфейсом базы данных создать нельзя, так как сработает исключение. И ты получаеш свою любимую функцию get_inst()   .   
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 27-02-2009 23:08 от Finch »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #15 : 27-02-2009 23:19 »   | 
								
								 | 
							  
							 
							Finch,  Вот! Это уже похоже на то, что мне нужно! Завтра попробую реализовать...    
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Finch
							
								Спокойный 
								Администратор
								
								 
								  Offline
								Пол:   
								
								Пролетал мимо
								
								
								
								
								
							  
						 | 
						
							
								  | 
								
									
									 « Ответ #16 : 27-02-2009 23:28 »   | 
								
								 | 
							  
							 
							А нет, вру. Будет лажа. Лучше шаблон убрать.  typedef enum _DB_TYPE {      DATABASE_1,      DATABASE_2 }DB_TYPE;
  class CDatabase { public:      CDatabase()      {        std::cout << "Constructor" << std::endl;        if (_inst) throw 0;        switch(db_type)  	{          case DATABASE_1:            _inst = new CDatabase1();            break;         case DATABASE_2:            _inst = new CDatabase2();            break;        }            }
      ~CDatabase()      {        std::cout << "Destructor" << std::endl;        delete _inst;        _inst = NULL;     }          static IDatabase *get_inst() {return _inst;}  private:     static IDatabase *_inst; };
  IDatabase *CDatabase::_inst=NULL;
 Я конечно не вижу полной картины твоей программы. Исхожу из того что ты сказал.  1) Держать все время открытым соединение с базой данных не рекомендуют. Соединение делается только по мере надобности. Так ты во первых уменьшаеш нагрузку на сервер баз данных, во вторых держиш соединение в актуальном состоянии.  2) Так сильно сцеплять классы между собой также не рекомендуют. Классы должны выражать собой черные яшики. Известно что входит и выходит. Но что внутри, не известно. Также минимальная зависимость от внешних факторов. Таким образом достигается почти безболезненая переносимость кода. И возможность расширения системы.   
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 28-02-2009 06:33 от Finch »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Не будите спашяго дракона.              Джаффар (Коша)  
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									 « Ответ #17 : 28-02-2009 08:32 »   | 
								
								 | 
							  
							 
							Finch, Да пожалуй без шаблонов выглядит лучше    Что касается подключения, то основной БД в программе будет SqLite, а она вообще встраеваемая и не имеет сервера. Да и можно ввести еще одну статическую функцию dbClose(); чтобы иметь возможность закрывать БД, но только там где это необходимо.  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
									« Последнее редактирование: 28-02-2009 08:37 от RuNTiME »
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						
							Dimka
							
								Деятель 
								Команда клуба
								
								 
								  Offline
								Пол:   
								
								
								
								
								
							 
						 | 
						
							
								  | 
								
									
									 « Ответ #18 : 28-02-2009 08:37 »   | 
								
								 | 
							  
							 
							Реализуйте модуль DatabaseConnectionModule, состоящий из заголовочного файла - интерфейса модуля, и файла реализации - внутренней части модуля, недоступной другим пользователям.
  Создайте класс DatabaseConnectionProvider со статическим методом getConnection и закрытыми конструкторами и деструкторами. Он будет отвечать за доступность соединения во всей программе. Соединение будет описываться классом IDatabaseConnection, но внутри класса должно быть выбран определённый класс DatabaseConnection для создания экземпляра.
  Создайте класс IDatabaseСonnection с открытыми абстрактными методами.
  Объявите вышеперечисленные классы в заголовочном файле.
  Создайте разные классы DatabaseConnection, открыто наследующие IDatabaseConnection, реализуйте в них абстрактные методы.
  Эти классы не объявляйте в заголовочном файле, чтобы другие пользователи не получили к ним доступ. 
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел) 
						 | 
					 
				 
			 |  
		 
	 | 
	
		
		
			
				
					
						| 
							RuNTiME
							
						 | 
						
							
								  | 
								
									
									«  Ответ #19 : 28-02-2009 08:41 »    | 
								
								 | 
							  
							 
							dimka,  все реализовано точно так как ты и пишешь, за исключением объявления служебных классов в .h файле... мысль хорошая убрать их оттуда    так и поступим.  
						 | 
					 
					
						
							
								| 
								 | 
							 
								| 
								 | 
								
									 
									Записан
								 | 
							  
							 
							Любимая игрушка - debugger ... 
						 | 
					 
				 
			 |  
		 
	 | 
	 |