Класс и класс-потомок: как проще работать со свойствами-строками?
Сказанное вот здесь
об удобстве использования объектов встроенного класса string
вместо заданных указателями char *
си-строк остаётся верным и при разработке иерархии классов.
Приведём в качестве примера файлы консольного проекта C++ (проверен в Studio 2019), реализующего класс Phone
и его потомка - класс Communicator
сначала с помощью указателей char *
(для простоты оба класса описаны в одном заголовочном файле), а потом с помощью строк string
.
Создавать лучше пустой проект, а потом добавлять туда элементы.
Нужно понимать, что при чтении-записи файлов с объектами string
нужно или заранее ограничивать длину хранимой строки или конвертировать строку в массив символов char
.
файл main.h
#ifndef PHONE_H #define PHONE_H #include <iostream> #include <windows.h> #include <string> #include <cstdlib> #include <cstring> #include <clocale> /*информация о сотовых телефонах: производитель, модель, разрешение, вес, цена. */ class Phone { private: char* vendor; char* model; int width, height, weight; float price; public: Phone(const char* vendor, const char* model, int width = 0, int height = 0, int weight = 0, float price = 0.); Phone(const Phone& from); virtual ~Phone(); char* getInfo(); //производитель+модель void setScreenSize(int width, int height); char* getScreenSize(); int getWeight() { return weight; } void setWeight(int weight) { this->weight = weight; } float getPrice() { return price; } void setPrice(float price) { this->price = price; } virtual void show(); }; Phone::Phone(const char* vendor, const char* model, int width, int height, int weight, float price) { this->vendor = new char[strlen(vendor) + 1]; strcpy(this->vendor, vendor); this->model = new char[strlen(model) + 1]; strcpy(this->model, model); setScreenSize(width, height); setWeight(weight); setPrice(price); } Phone::Phone(const Phone& From) { this->vendor = new char[strlen(From.vendor) + 1]; strcpy(this->vendor, From.vendor); this->model = new char[strlen(From.model) + 1]; strcpy(this->model, From.model); this->setScreenSize(From.width, From.height); this->setWeight(From.weight); this->setPrice(From.price); } void Phone::setScreenSize(int width, int height) { this->width = width; this->height = height; } Phone::~Phone() { delete[] model; delete[] vendor; } char* Phone::getScreenSize() { char* buf = new char[16]; sprintf(buf, "%dx%d", this->width, this->height); return buf; } char* Phone::getInfo() { char* buf = new char[strlen(this->vendor) + strlen(this->model) + 2]; sprintf(buf, "%s %s", this->vendor, this->model); return buf; } void Phone::show() { printf("\n"); char* info = this->getInfo(); puts(info); printf(" (%s, %d, %.2f)", this->getScreenSize(), this->getWeight(), this->getPrice()); } class Communicator : public Phone { protected: char* os; public: inline Communicator(const char* vendor, const char* model, int width = 0, int height = 0, int weight = 0, float price = 0., const char* os = NULL) : Phone(vendor, model, width, height, weight, price) { setOS(os); } void setOS(const char* os) { if (os) { this->os = new char[strlen(os) + 1]; strcpy(this->os, os); } } Communicator(const Communicator& from) : Phone(from) { setOS(from.os); } ~Communicator() { delete[] os; } char* getOS() { return this->os; } void show() { this->Phone::show(); printf(" %s", this->getOS()); } friend Communicator random_communcator(); }; #endif
файл main.cpp
#define _CRT_SECURE_NO_WARNINGS #include "main.h" using namespace std; Communicator random_communcator() { //функция-друг - вернет коммуникатор со случайными характеристиками const char* marks[3] = { "Nokia","Philips","LG" }; Communicator c((const char*)marks[rand() % 3], (const char*)"Unknown", 100+rand()%100, 100 + rand() % 100, 100 + rand() % 100, 1000 + rand() % 1000, (const char*)""); return c; } int main(void) { setlocale(LC_ALL, "Russian"); Communicator c0((const char *)"LG", (const char*)"500", 90, 125, 200, 10500, (const char*)"Android"); c0.show(); //создание непосредственное Communicator* c1 = new Communicator((const char*)"Dell", (const char*)"Streak", 640, 480, 300, 15000, (const char*)"Android"); //создание через new и указатель c1->show(); Communicator c2 = *c1; //присваивание объектов c2.show(); delete c1; //удаление объектов //a с0 так удалять нельзя - он в стеке, а не в куче Communicator c3 = random_communcator(); //возврат объекта из функции void (Communicator:: * pointer)(void) = &Communicator::show; (c3.*pointer)(); //вызов метода через указатель на функцию //виртуальный метод - дополнили show родителя cout << endl; system("pause"); return 0; }
Ниже показана версия этого проекта с описанием каждого класса в отдельной паре файлов .h
и .cpp
и с использованием строк string
вместо char *
(это позволяет
значительно упростить код, связанный с управлением памятью). Обратите внимание, что имена .cpp
и .h
файлов должны совпадать с именами
описанных в них классов.
Файл Phone.h
#ifndef PHONE_H #define PHONE_H #include <iostream> #include <string> using namespace std; class Phone { protected: string vendor, model; int width, height, weight; float price; public: Phone(string vendor, string model, int width = 0, int height = 0, int weight = 0, float price = 0.); Phone(Phone& from); virtual ~Phone(); string getInfo(); //производитель+модель void setScreenSize(int width, int height); string getScreenSize(); inline int getWeight() { return weight; } inline void setWeight(int weight) { this->weight = weight; } inline float getPrice() { return price; } inline void setPrice(float price) { this->price = price; } virtual void show(); }; #endif
Файл Phone.cpp
#include "phone.h" Phone::Phone(string vendor, string model, int width, int height, int weight, float price) { this->vendor = vendor; this->model = model; setScreenSize(width, height); setWeight(weight); setPrice(price); } Phone::Phone(Phone& From) { this->vendor = From.vendor; this->model = From.model; this->setScreenSize(From.width, From.height); this->setWeight(From.weight); this->setPrice(From.price); } void Phone::setScreenSize(int width, int height) { this->width = width; this->height = height; } Phone::~Phone() {} string Phone::getScreenSize() { return to_string(this->width) + "x" + to_string(this->height); } string Phone::getInfo() { return this->vendor + " " + this->model; } void Phone::show() { cout << endl << this->getInfo() << "," << this->getScreenSize() << "," << this->getWeight() << "," << this->getPrice(); }
Файл Communicator.h
#ifndef COMMUNICATOR_H #define COMMUNICATOR_H #include <iostream> #include <string> #include "phone.h" using namespace std; class Communicator : public Phone { protected: string os; public: inline Communicator(string vendor, string model, int width = 0, int height = 0, int weight = 0, float price = 0., string os = "") : Phone(vendor, model, width, height, weight, price) { setOS(os); } inline void setOS(string os) { this->os = os; } inline Communicator(Communicator& from) : Phone(from) { setOS(from.os); } ~Communicator() {} inline string getOS() { return this->os; } void show(); friend Communicator & random_communicator(); }; #endif
Файл Communicator.cpp
#include "communicator.h" void Communicator::show() { this->Phone::show(); cout << "," << this->getOS(); }
Файл main.cpp
#define _CRT_SECURE_NO_WARNINGS #include <windows.h> #include "communicator.h" using namespace std; Communicator& random_communicator() { //функция-друг - вернет коммуникатор со случайными характеристиками const char* marks[3] = { "Nokia","Philips","LG" }; Communicator* temp = new Communicator((const char*)marks[rand() % 3], (const char*)"Unknown", 100 + rand() % 100, 100 + rand() % 100, 100 + rand() % 100, 1000. + rand() % 1000, (const char*)"NoOS"); return *temp; } int main(void) { setlocale(LC_ALL, "Russian"); SetConsoleCP(1251); SetConsoleOutputCP(1251); Communicator c0((const char *)"LG", (const char*)"500", 90, 125, 200, 10500., (const char*)"Android"); c0.show(); //создание непосредственное Communicator* c1 = new Communicator((const char*)"Dell", (const char*)"Streak", 640, 480, 300, 15000., (const char*)"Android"); //создание через new и указатель c1->show(); Communicator c2 = *c1; //присваивание объектов c2.show(); delete c1; //удаление объектов //a с0 так удалять нельзя - он в стеке, а не в куче Communicator c3 = random_communicator(); //возврат объекта из функции void (Communicator:: * pointer)(void) = &Communicator::show; (c3.*pointer)(); //вызов метода через указатель на функцию //виртуальный метод - дополнили show родителя cout << endl; system("pause"); return 0; }
Наконец, реализуем (также для простоты в одном файле) класс Class
с числовым и строковым свойствами, а также класс-потомок Class2
, добавляющий к свойствам родителя собственное свойство типа bool
. Покажем вызовы всех конструкторов (без аргументов, с аргументами, копирования), работу с функциями-друзьями класса, косвенный вызов методов класса через указатели на функции.
#include <iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; class Class { //Класс-предок protected: //т.к. будут потомки int val; string str; public: Class() : val(0), str("") {} //конструктор без аргументов Class(int, string = ""); //конструктор с аргументами Class(Class& from); //конструктор копирования virtual ~Class() {} int getval() { return val; } string getstr() { return str.length() == 0 ? "null" : str; } void setval(int val) { this->val = val; } void setstr(string str) { this->str = str; } virtual string toString(string hdr = "") { return hdr + std::to_string(this->val) + " " + this->getstr(); } }; Class::Class(int val, string str) { this->setval(val); this->setstr(str); } Class::Class(Class& from) { this->setval(from.val); this->setstr(from.str); } class Class2 : public Class { //Класс-потомок protected: bool state; public: Class2() : Class() { setstate(false); } Class2(int val, string str = "", bool state = false) : Class(val, str) { setstate(state); } Class2(Class2& from) : Class(from) { setstate(from.state); } ~Class2() {} void setstate(bool state) { this->state = state; } bool getstate() { return state; } string toString(string = ""); friend void state_on(Class2*); //функции-друзья friend Class2& random_object(); //для иллюстрации вызова через указатель: int inc() { return ++this->val; } int dec() { return --this->val; } }; string Class2::toString(string hdr) { string s = this->Class::toString(hdr) + " "; s += (this->getstate() == true ? string("true") : string("false")); return s; } void state_on(Class2* obj) { obj->state = true; //имеет доступ к защищённому свойству, т.к. друг класса! } Class2& random_object() { const string abc = "abcdefgh"; Class2* temp = new Class2(rand() % 10, abc.substr(rand() % 3, 1 + rand() % 5), true); return *temp; //Следует возвращать именно ссылку на динамичеки созданный //объект, чтобы работало "по цепочке" } int main() { Class* a = new Class(); //1-й конструктор Class* b = new Class(1, "Rediskin"); //2-й конструктор Class c = *b; //конструктор копирования c.setstr("Redkin"); cout << a->toString("A: ") << endl; cout << b->toString("A: ") << endl; delete b; //деструктор cout << "C: " << c.getval() << " " << c.getstr() << endl; Class2 a2; //1-й конструктор Class2* b2 = new Class2(2, "Rediskina", false); //2-й конструктор Class2 c2 = *b2; //конструктор копирования state_on(&c2); //вызов "друга" srand(time(NULL)); //правильный запуск генератора с.ч. cout << a2.toString("A2: ") << endl; cout << b2->toString("B2: ") << endl; cout << c2.toString("C2: ") << endl; Class2 d2 = random_object(); //d2 - случайный объект cout << d2.toString("D2: ") << endl; int (Class2:: * ptr) (void) = &Class2::inc; //Указатель на функции класса 2 с типом int и без аргументов (d2.*ptr)(); //вызов метода через указатель на функцию cout << d2.toString("D2: ") << endl; ptr = &Class2::dec; //Поставил указатель на другую функцию (d2.*ptr)(); //теперь косвенно вызвана функция dec cout << d2.toString("D2: ") << endl; cin.get(); return 0; }
04.03.2020, 13:06 [1394 просмотра]