Работа с классами в Си++ = пример программы

{общий ход решения и советы здесь}

// pcmodel.cpp : Defines the entry point for the console application.
//
    /*
    12. Персональный компьютер. Реализуемые объекты:
    процессор, память, системная плата, устройства ввода вывода.
    Реализовать процедуры ввода/вывода с учетом возможностей оборудования.

    Уточнение =
    Для каждой задачи необходимо реализовать объектную модель.
    В реализации задач должны присутствовать наследование, агрегация и др. взаимодействия классов.
    Общение с пользователем осуществляется через консоль путем вызова
    наиболее значимых методов классов, позволяющих увидеть основную функциональность задачи.
    Для моделирования динамики процессов можно использовать повторяющиеся вызовы пользователем.
    */

#include < Windows.h>  // только ради использования функции Sleep()  =))

#include < string > // не забываем подключить для работы со строками
#include "stdafx.h"
#include < string.h >
#include < vector > // подключаем чтобы работать с векторами
// далее заголовки самописных функций

void showmess(char*, char*); // для вывода "системных" сообщений
 void  exitprogram(); // красиво завершат работу программы)

 char *simple_read_all( char *  , size_t ); // читает строчку произвольного размера 

// моделируемого нами компьютера в консоль
void startmodel();/*демонстрацию функционала программы*/
int inputcl(void);

int _tmain(int argc, _TCHAR* argv[])
{
	startmodel();

   return 0;
}



/*далее массив классов , и их потомков демонстрирующих
базовые примеры работы с классами в си++ - а также , агрегацию, наследование
и другие взаимодействи классов*/

/*для начала - демонстрация наследования в СИ++

ниже - примеры трёх уровней адсракции для описания процессоров
точнее два адстрактных уровня - это процессор вообще и второй слой абстракци - 
это процессор семейства интел.
последний же класс в жанном примере это уже конкретный процессор
марки интел*/

class CPU // прародитель для классов, описывающих реализацию процессоров
{
public:
   CPU(char* design,int freq)// сигнатура - формат констурктора
   { CPU::design=design;
   CPU::freq=freq;} // реализация конструктора прямо внутри тела класса
   // об особенностях описания функции внутри класса читайте на этом же ресурсе
   
   char*  design; // архитектура (пусть с точки зрения программирования)
   int freq; // frequency частота быстродействия в гигагерцах
}; 



/*далее пример паблик наследования
этот класс обощённо описывает семейство
процессоров Intel - это уже более конктретно*/
class Intel: public CPU // а вот уже некий конкретный проц от интел
{
public:	
Intel(char* , int); // конструктор для класса данной можели опишем снаружи
int freq; // frequency частота быстродействия в гигагерцах
protected:
  const char * firm ; // защищённое поле фирмы -
  //переопределить его извне в потомках будет невозможно
};

// далее указываем базовый конструтор
Intel::Intel(char* design,int freq):CPU(design,freq) // обычно функция класса описывается снаружи - как и в этом случае
{
    Intel::design=design;
	Intel::freq=freq;
	firm="Intel";// вот оно- неизменяемое значение для любого из семейства процессоров Intel
   // заметьте  - если конфликта имён нет, то можно не указывать пространство имён
	/*по умолчанию функциям-членам класса доступны пространства имён класса
	а уж конструктор  -как ни крути фукция-член класса*/
}


/*далее опять пример паблик наследования
ЭТО КЛАСС УЖЕ КОНКРЕТНЙ МОДЕЛИ ПРОЦЕССОРА*/
class Intel2000 : public Intel // а вот уже некий конкретный проц от интел
{
public:	
Intel2000(char*,int,int); // конструктор для класса данной можели опишем снаружи

private:
   int id; // пусть серийный номер будет не публичен 
   // иные объекты описывающие компоненты компьютера 
   // не смогут узнать его))
};



Intel2000::Intel2000(char* design,int freq,int id):Intel(design,freq) // конструктор для предыдущего класса
// заметьте, что model CPU(model) - как и иные возможные параметры должен 
// фигурировать в сигнатуре базовог коструктора-
/* именно на оснавании базового констуктора интерпретатор определит тип параметра -
потому собственно для родительского конструтора и указывается только одно имя - без типа*/
{
	 design="x86";// пусть эта модель соответсвует архитектуре x86
	Intel2000::id=id; // запоминаем серийный номер - его передадим уже когда будем создавать класс
}

//--------дальше один только один вровень абстракции для описания ОПЕРАТИВНОЙ ПАМЯТИ
/* */

class RAM // обобщённый класс для описания модулей оперативной памяти (абстракция)
{
public: 
	RAM(int size)
	{
		RAM::size=size;
	}
		int size;// объём оперативной памяти - значение следует передать в констуртор 

};

class RAM1 : public RAM // первый конкретный модуль оперативы
{
public: 
	RAM1(int size):RAM(size)
	{
		RAM1::size=200; //независимо от переданного параметра- пусть жто 200 мегабайт для модуля RAM1
	}
	
};


class RAM2 : public RAM // второй  конкретный модуль оперативы
{
public: 
	RAM2(int size):RAM(size)// описываем прямо в теле класса - читайте об особенностях такого описания в "инете"
	{
		RAM2::size=1000; //независимо от переданного параметра- пусть жто 1000 мегабайт для модуля RAM1
	}
	
};

// далее опишем класс материнских плат и создами пару конкретных моделей

/*
к материнской плате подключаются 
*/
class Mboard // не будем для этого класса описывать никакого конструктора
{
public:
	int mmcount; // max modules count =максимальное число модулей
	 //памяти которое поддерживат данная плата
	int msize;//memory size=объём памяти, оперативы, закреплённой на плате
	int freemem;// объём свободной оперативной памяти
	std::string buffer; // содержит символы, записанные в память

	Mboard() // всё-таки добавил конструктор для инициалл. парамеров
		{
         mmcount = 0;
		 msize=0;
		 freemem=0;
		 buffer=""; // изначально - это просто пустая строка)
		 MemoryState();
	}
	std::vector < RAM > ramms; // массив модулей памяти, подключённых к материнской плате
	
	void Addm(RAM ramm)//Add module добавление модуля памяти
	{
		if (CheckModuleCount()) // если свободное место есть
		{
			 Mboard::msize=msize+ ramm.size; // прибавляем память к имеющейся
			 freemem = freemem+ ramm.size; // свободной памяти тоже прибавляется

			ramms.push_back(ramm); // размещаем модуль на системной плате
		     showmess("COMPLETE", "RAM was added !\n");
			 printf("You've added =  %i Mb\n",ramm.size);
			 MemoryState();
			 
		
		} else /*вообще по-идее далее можно было бы сгенеировать исключение
			   но мы просто ограничемся выводом в консоль уведомления о том, 
			   что все слоты для оперативной памяти заняты.
			   для этого я просто произведу вызов самодельной функции которая выводит
			   сообщение в консоль - опишем её как статическую в этом же классе-
			   её можно заменить на printf()*/ 
		{
           showmess("WARNING","No more free slots for RAM ! \n");
		    MemoryState();

		}
	}

	void WriteData(std::string str) // пишем в память
	{
		/*мы уножаем на 20 - просто чтобы увеличить 
		"вес" каждого символа в оперативной памяти нашего
		воображаемого компьютера*/
		if ((str.size()*20)<=freemem)
		{
		Mboard::buffer+=str;
		freemem=freemem-str.size()*20;//свободная память уменьшается
		 MemoryState(); // выводим инфу о состоянии памяти
		} 
		else // если строка для записи слишком велика, то =
		{
			printf("This string is too big= you need free space = %i Mb ! \n",str.size()*20);
			MemoryState(); 
		}
	}

	void FreeMemory() // выполняем очистку памяти
	{
		
		freemem=freemem+buffer.size()*20;// объём свободной памяти увеличивается
		buffer=""; //стираем всю информацию
		MemoryState(); // выводим инфу о состоянии памяти
	}

	void MemoryState() // выводит информацию о сотоянии памяти
	{
		printf("-------[memory--info]---------------------\n");
		printf(">>Symbols in buffer =  %s \n",(char *)  buffer.c_str()); 
		printf(">>FREE memoty size =  %i \n",Mboard::freemem);
		printf(">>Total memoty size =  %i \n",Mboard::msize);
		printf("------------------------------------------\n");
	}

protected: // впервые в этом примере мы используем protected!
	/* это сделано для того, чтобы с одной стороны элементы класса описанные ниже
	были доступны классам-потомкам (с private такого не получится)
	а с другой стороны, чтобы внешние классы данные методы вызвать не могли
	и опратиться к полям - опять же не могли.
	Так как здесь быдут располагаться методы и данные(хранящиеся в полях)
	используемые для внутренних вычислений
	и операции класса - остальным до них дела быть не должно)))*/
	
	int CheckModuleCount() // проверяем - есть ли ещё свободные слоты 
		// для оперативной памяти
	{

		int result=0; // 
		if (ramms.size()<=mmcount) result= 1;
		return result; 
	}


};

class Mboard1 : public Mboard // это плата с фиксированным числом слотов
{
public:
	Mboard1() // так как базовый конструктор описан не был 
    // то и указывать его не надо	
	{
		 mmcount=4; // пусть у неё будет четыре слота
	}
};

class Mboard2 : public Mboard // это вторая  плата с фиксированным числом слотов
{
public:
	Mboard2() // так как базовый конструктор описан не был 
    // то и указывать его не надо	
	{
		 mmcount=3; // а у этой - три слота
	}
};

/*далее класс, описывающий дисплей  - просто устройство вывода
оно будет предъявлять требования к оперативной памяти*/

class Display
{
public:
	int neededmem;
	Mboard* mboard;
	Display()
	{
		neededmem = 20;// пусть этот дисплей требует 20 мегабайт
    }
    Display(Mboard *mboard)// конструктор с "быстрым подключением к материнке"))
	{
		Display::mboard=mboard;
		neededmem = 20;// пусть этот дисплей требует 20 мегабайт
    }
	 void Connect(Mboard* mboard) // для подключения к компу после создания самого дисплея
	 {
		  Display::mboard=mboard;
	 }
	 void ShowData() // для подключения к компу после создания самого дисплея
	 {
          // опять же обратите внимание на работу с указателями
		 // (*mboard) - это как раз "данные по указателю"
		 /*понимание правил и принципов работы с указателя 
		 принципиально важно в Си и Си++*/
		 char * str = (char *)(*mboard).buffer.c_str() ;
		 printf("\n=======================================\n");
		   printf("=============DISPLAY==========================\n");
		 printf("\n %s \n",str);
		 printf("\n=======================================\n");
		   printf("=======================================\n");
		   (*mboard).MemoryState();
	 }
	
};

/* опишем класс, позволяющий моделирующий устройство ввода - клавиатуру.
этот класс будет просто записывать строки в память*/
class Keyboard
{
public:
	Mboard * mboard;// нужно же клаву к чему-то присоединять
	Keyboard() // оставим в том числе и конструктор по умолчанию
	 {
	 }
	 Keyboard(Mboard* mboard)
	 { 
		 Keyboard::mboard=mboard;
	 }
	 void WriteData(std::string str)
	 {
		 (*mboard).WriteData(str); // пишем данные
	 }
	 void Connect(Mboard* mboard) // для подключения к компу после создания самой клавы
	 {
		  Keyboard::mboard=mboard;
	 }
};

// ну пора бы уже написать АГРЕГИРУЮЩИЙ КЛАСС - компьютер
/*здесь мы не будем демонстрировать наследование - просто напишем один 
класс, который продемонстрирует.*/
class PC
{
   public:
	  Mboard mboard;// суда заряжаем класс описывающий материнскую плату
	  Display display;// для подключения дисплея
	  Keyboard keyboard; // для клавы
	  CPU cpu;// процессор - правда его комп получает по-умолчанию 
	  // - указываем используемый конструктор

	 PC (Mboard mboard):cpu("x86",2) // "x86" и 2 - параметры по умолчанию  
		 /* для начала сборки компьютера
		в его конструктор достаточно просто передать объект "материская плата"
		причём любой модели 
		ВНИМАНИЕ!= после параметров конструктора идёт список инициаллизации-
		он необходим для всех членов-классов у которых
		нет конструктора "без аргументов" (= назв.- "конструтор по-умолчанию") 
		- такой (= "пустой"  -не принимающий аргументов)
		конструктор надо явно прописыть для всех классов,
		определяющих собственнный конструтор с параметрами.
		Если же конструтор по умлочанию отсутствует, то
		следует указать принимаемые аргументы в списке 
		инициаллизации -ч то и сделано выше для cpu =
		:cpu("x86",2)
		 */
	 {
		 PC::mboard=mboard; // закрепляем её))
         Intel2000 * newcpu =new Intel2000("x86",3,123654); // СОЗДАНИЕ ОБЪЕКТА В(на)) СИ++ ))
		 // далее переопределим те данные, 
		 //что были в списке инициализации и снабдим наш класс конкретным процессором
		  PC::cpu = *newcpu;// "прикручиваем" процессор - а вот и переопределение
		  // выше происходит замена одного указателя на другой - так как фактически 
		  // объект это указатель из несокльких байт
// cpu = newcpu; 
	 }
	 PC():cpu("x86",2) // всё-таки определим конструктор по-умолчанию
	 {
		  
	 }
	 void AddDisplay(Display display)
	 {
		 if (display.neededmem<=PC::mboard.freemem)
			 {
				// display.Connect(PC::mboard);// подключаем дисплей к материнской плате
				 PC::display=display;
				 PC::mboard.freemem=PC::mboard.freemem - display.neededmem;//уменбшаем объём свободной памяти
				 showmess("PC = OK","display was added to your PC =))");
				 mboard.MemoryState();
		  }
		 else showmess("PC_ERROR","you haven't enough memory to add this display");
	 }
	 void AddKeyboard(Keyboard keyboard)
	 {
		  PC::keyboard=keyboard;
//		  keyboard.Connect(mboard);
		  showmess("PC = OK","Keyboard was added to your PC =))");
		  mboard.MemoryState();
	 }
};

/* далее СУПЕР-КЛАСС котроый выступит этакой моделью
 магазина комплектующих для комьпютера*/

class PCshop
{
  public:
	  PC yourpc;
	  int hasmboard; //признак того что уже выбрали материнку
	  int haskeyboard;//признак того что уже выбрали клаву
	   int hasdisplay;//признак того что уже выбрали клаву

	   PCshop():yourpc() // конструктор
	   {
		  hasmboard=0;
		  haskeyboard =0;
		  hasdisplay=0;
	   }
	  

	  void ShowMainMenu()
	  {
		  showmess("MENU","this's PC shop menu=");
		  if (!hasmboard) showmess("(1)"," press 1 to choose mather board");
		  else
			  {
				  showmess("(2)"," press 2 to ADD  a RAM module to mather board");
				  showmess("(3)"," press 3 to ADD a display to your PC");
				  showmess("(4)"," press 4 to ADD a keyboard to your PC");
				  if (haskeyboard) showmess("(5)"," press 5 to work with your PC"); 
				  showmess("(0)"," press 0 to EXIT-> ");
			  }   

		  char ch = getchar(); // принимаем символ из стандартного потока ввода
		    inputcl(); // чистив поток ввода от ЭНТЕРА
		   CallMeinMenuFunc(ch);
	  }
	  void CallMeinMenuFunc(char ch)
	  {
		  switch (ch)  // ВАРИАНТЫ ПАРАМЕТРОВ
                {
                 case '1': // это или иное значение которому может быть равен параметр
                     {
						 ShowMboardChoiceMenu();
                     }
                    break; // выход из перебора вариантов -иначе будет рассматриваться case 2
 
                 case '2': ShowRamChoiceMenu();
					 break;
			     case '3': 
					 {
						 Display* display=new Display(&(yourpc.mboard));
						 //*display.Connect(yourpc.mboard);// поключаем 
						 PCshop::yourpc.AddDisplay(* display);
						 hasdisplay=1;

					 }
					 break;
			 case '4': 
					 {
						 //обратите внимание как мы полeчаем сам адрес элемента в памяти
						 Keyboard* keyboard=new Keyboard(&(yourpc.mboard)); 
						 PCshop::yourpc.AddKeyboard(* keyboard);
						 haskeyboard=1;

					 }
					 break;

		   case '5': 
					 {
						 CallPCWorkFunc(); // контекстное меню и операции компьютера

					 }
					 break;
					   case '0': exitprogram();

		  }
	  }
	   void  ShowRamChoiceMenu()
	   {
		    showmess("Ram Choice menu","this's MboardChoice menu=");
			  showmess(" (1)","press 1 to choose RAM1");
			  showmess(" (2)","press 2 to choose RAM2");

			    char ch = getchar(); // принимаем символ из стандартного потока ввода
				  inputcl(); // чистив поток ввода от ЭНТЕРА
		    CallRamChoiceFunc(ch);

	   }

	  int   CallPCWorkFunc() // обработчик для контекстного меню работы с компьютером
	   {
		  for (;;)
		  {
			  yourpc.mboard.MemoryState(); 
			  showmess("YOUR PC","working menu = ");
			  showmess(" (1)","press 1 to write data");
			  showmess(" (2)","press 2 to show data");
			  showmess(" (3)","press 3 to remove data");
			  showmess(" (4)","press 4 to come back in PC shop menu");

			  char ch = getchar(); // принимаем символ из стандартного потока ввода
				  inputcl(); // чистим поток ввода от ЭНТЕРА

		   switch (ch)  // ВАРИАНТЫ ПАРАМЕТРОВ
                {
                 case '1': // 
                     {
              
				// строчкой ниеж чиатем строку любой длинны из консоли
						char*  chs  = simple_read_all("user text input reading....",5); 
						 std::string str =chs; 
						 yourpc.keyboard.WriteData(str);// вот такой иерархический вызов для записи))

                     }
                    break; //
					case '2': // 
                     {
						 if (hasdisplay) 
						 {
							 yourpc.display.ShowData();
						 }
						 else showmess("ERROR"," Go in PC shop and buy display for yout PC ! ))");

                     }
                    break; 
					case '3': // 
						{
						 yourpc.mboard.FreeMemory();

                     }
                    break; 
					case '4': // 
						{
						  return 1; // выходим в контекст PC-shop

                     }
	          }
		    inputcl();
		   
	      }
		  return 1;
	  }
	   void   CallRamChoiceFunc(char ch) // вызовы для выбора модулей оперативной памяти
	   {

		   switch (ch)  // ВАРИАНТЫ ПАРАМЕТРОВ
                {
                 case '1': // 
                     {
						RAM1 * ram1= new RAM1(20);
				
						PCshop::yourpc.mboard.Addm(*ram1); //

                     }
                    break; //
 
                  case '2':
					  {
						RAM2 * ram2= new RAM2(20);
						PCshop::yourpc.mboard.Addm(*ram2); //
                     }
                    break; 

	             }
	   }


		  void ShowMboardChoiceMenu() // меню выбора материнской платы
		  {
			  showmess("MboardChoice","this's MboardChoice menu=");
			  showmess("MboardChoice","press 1 to choose mboard1");
			  showmess("MboardChoice","press 2 to choose mboard2");

			  char ch = getchar(); // принимаем символ из стандартного потока ввода
			  inputcl(); // чистив поток ввода от ЭНТЕРА
		    CallMboardChoiceFunc(ch);
		  }
		 
		  
		  void CallMboardChoiceFunc(char ch) // вызовы для выбора материнской платы.
		  {
			  switch (ch)  // ВАРИАНТЫ ПАРАМЕТРОВ
                {
                 case '1': // 
                     {
						Mboard1* mboard1= new Mboard1();
						PC* somepc = new PC(*mboard1);
						PCshop::yourpc=* somepc; // прикреплям материнску плату 
						showmess("OK =PC-shop","Mboard1 was added !");
						hasmboard=1; // фиксируем факт выбора системной платы
                     }
                    break; //
 
                  case '2':
					  {
						Mboard2* mboard2= new Mboard2();
						PC* somepc = new PC(*mboard2);
						PCshop::yourpc=* somepc; /* прикреплям материнску 
												 плату  образца2 */
						showmess("OK =PC-shop","Mboard2 was added !");
						hasmboard=1; // фиксируем факт выбора системной платы
                     }
                    break; 
		      }
		  }
};
//---далее функция которая запускает демонстрацию функционала программы-----------

void startmodel()
{
	PCshop* shop = new	PCshop();
	for (;;)
	{
		(*shop).ShowMainMenu();
	}
}

// -----======================------ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ=---========================---------------

// функция выводящая сообщение в консоль
void showmess(char* messtart ,char* text) // start word of message
{
	int i=1;
	 std::string messt = messtart; // конвертировать в string  можно неявно
	  std::string txt = text; // конвертировать в string  можно неявно

	  // далее склеиваем переданный параметы немного их облагородив
	 txt = "[(!)" + messt +" = " + txt +" ]\n";// а вот и КОНКАТЕНАЦИЯ  в СИ++

	 char* message = (char *)  txt.c_str();/*переводим обратно в char * 
				чтобы использовать  далее printf*/
	
	 printf(message); // выводим полученную строку в консоль
}

 void  exitprogram()
 {
	 Sleep(800); ////пауза
	 printf("\n\n"); 
	 printf("Bye! =)"); // прощаемся с пользователем =)
	 printf("\n\n"); 
	 Sleep(1000); //  ещё пауза!))



	  exit(0); // завершаем проргамму
 }

 #if defined(_WIN32) || defined (_WIN64)
    #define  IN_WINDOWS 1
#endif
/* эта функция очистит стандартный поток ввода stdin (обычно - консоль)*/
int inputcl(void)
{
   if (IN_WINDOWS) fflush(stdin); // если мы в виндоус - просто очищаем поток
   else // если же нет  -то , так сказать, работаем дальше
   {
    printf("\n[%s]\n", "If nothing take place - press enter to continue the program execution.");
    while (getchar() != '\n'); /*remove \n  or other useless data*/
   }
   return 0;
}


char *simple_read_all( char * prompting_message , size_t user_block_size)  /* make sure that the input is clear when you call this function */
 {           /**/
    char *result;
     
    /*variables and  initialization*/
    size_t block_size ;
    if (user_block_size)  block_size = user_block_size;  /*если пользователь указал собственный , то выбираем его как рабочий*/
       else  block_size = 5; /*иначе используем стандартный блок длинной = 5*/
 
      char  *block, *temp;
    size_t size = block_size;
    size_t n = 0; /* здесь будет храниться размерность считанной строки*/
    /*printf("\n=[%s]", "[" .prompting_message. " ]" . "\n");*/
 
    if ( prompting_message) 
    {
      printf("%s \n", prompting_message); /* show message wich specify the reason of input reading - for examp "input clearing" */
    }
     /*function itself */
      
    result = block = (char*)malloc(size); /*выделяем блоку и строке результата память длинной , равной длинне блока разового чтения*/
      
    if (!result) return NULL; /*если при выделении памяти произошла ошибка - отражаем это в переменной результата*/
      
    *result = '\0'; /* используем это  присвоивание как признак успешности - см. дальнейшие комментарии после цикла*/
      /*этот цик*/
    for (;;) /*этот цикл - ключевой в работе данной функции*/
    {
        if (fgets(block, block_size, stdin) == NULL) /*читаем блок данных стандартой длинн и размещаем данные в переменной "блок"*/
          break; /* если при чтении произошла ошибка   - выходим из цикла */
          
        n += strlen(block);
          
        if (result[n - 1] == '\n') /* если последний сивол считанной строки - это символ конца строки, то.....*/
        break; /*...выходим из цикла */
          
        size += block_size; /* наращиваем размерность строки результата на величину, равную длинне стадартного блока чтения*/
        temp = (char*)realloc(result, size); /* переразмещаем данные по адресу возвращаемой строки в новой области большего размера - сохраняем адрес этой области в вспомогаетльную переменную */
          
        if (temp == NULL)
        break;
          
        result = temp; /* сохраняем адрес новой области памяти из вспомогательной переменной в оригинальную переменную результата*/
        block = result + n; /* перемещаем указатель записи очередного блока данных на первую свободную ячейку памяти в новой области данных*/
    }
      
    if (result[0] == '\0') /* если в предыдущем цикле нам не удалось ничего прочитать - освобождаем память,которую "держит" указатель на строку-результат и отражаем неуспешность чтения на возвращаемом значении функции*/
    {
        free(result); /*...освобождаем память,которую "держит" указатель на строку-результат...... */
        result = NULL;/*....и отражаем неуспешность чтения на возвращаемом значении функции*/
    }
    if ( prompting_message)  printf("[%s] \n", "Data was read");  /* show message wich notify about the end of reading input" */
    return result;
}
 

vedro-compota's picture

_____________
матфак вгу и остальная классика =)