Указатели на объекты (классы) в Си++ - класс в качестве члена(элемента) класса - работа с указателями. Взаимодействие классов

Друзья. на практике оказывается важным понимания основных механизмов "налаживания взаимодействия" между классами в Си++

Прежде всего вспомним о нашей задаче, а точнее - о её решении в виде кода на Си++

Я прокомментирую основные моменты "логики" решения ,которые и иллюстрируют возможности взаимодействия классов в Си++:

  1. материнская плата агрегирует модули оперативной памяти и отвечает за все операции с памятью =
    // далее опишем класс материнских плат и создами пару конкретных моделей
    
    /*
    к материнской плате подключаются 
    */
    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; 
    	}
    
    
    };
  2. Класс под названием "PC" агрегирует в себе классы - материнской платы, клавиатуры, дисплея. =
    // ну пора бы уже написать АГРЕГИРУЮЩИЙ КЛАСС - компьютер
    /*здесь мы не будем демонстрировать наследование - просто напишем один 
    класс, который продемонстрирует.*/
    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();
    	 }
    };
  3. Класс клавиатуры - должен взаимодействовать с памятью, а потому необходимо "уведомить" класс о существовании материнской платы =
    /* опишем класс, позволяющий моделирующий устройство ввода - клавиатуру.
    этот класс будет просто записывать строки в память*/
    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;
    	 }
    };

    - внимательно изучите конструктор - вы передаём в него адрес в памяти по которому располагается класс материнской платы
    для клавиатуры это делается так (просто беру пример из листинга программы)=

    //обратите внимание как мы получаем сам адрес элемента в памяти
    						 Keyboard* keyboard=new Keyboard(&(yourpc.mboard)); 
    						 PCshop::yourpc.AddKeyboard(* keyboard);
    						 haskeyboard=1;
  4. дисплей также взаимодействует с компьютером через материнскую плату =
    /*далее класс, описывающий дисплей  - просто устройство вывода
    оно будет предъявлять требования к оперативной памяти*/
    
    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();
    	 }
    	
    };


Основной вывод:

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