БлогNot. Класс и класс-потомок: как проще работать со свойствами-строками?

Класс и класс-потомок: как проще работать со свойствами-строками?

Сказанное вот здесь об удобстве использования объектов встроенного класса 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 просмотра]


теги: учебное программирование c++

К этой статье пока нет комментариев, Ваш будет первым