БлогNot. C++: как поддерживать динамическое свойство класса и нужно ли это делать :)

C++: как поддерживать динамическое свойство класса и нужно ли это делать :)

Например, заданное указателем, с возможностью увеличить при необходимости память, занимаемую свойством.

Естественно, для этого придётся освободить память, которую свойство занимало ранее.

Простой листинг может выглядеть примерно так:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;

class A {
private:
 int n;
 char *s;
public:
 A(int n = 0) { this->n = n; this->s = nullptr; }
 A(int n, char *s = nullptr) {
  this->n = n;
  this->s = nullptr; //инициализируем свойство перед определением его значения
  this->setname(s);
 }
 int setname(char *);
 ~A() { delete[] this->s; }
 void show() { 
  int len = strlen(this->s);
  cout << endl << this->n << ", " << (len==0?"(null)":this->s);
 }
};

int A::setname(char *s) {
 if (s == nullptr) return 1; //Попытка инициализировать пустым значением
 if (this->s) { //Освобождение памяти, если свойство было установлено
  delete[] this->s;
  this->s = nullptr;
 }
 if (this->s == nullptr) { //Инициализация пустого свойства строкой
  int len = strlen(s) + 1;
  this->s = new char[len];
  if (this->s != nullptr) { //Удалось выделить память
   strncpy(this->s, s, len);  return 0;
  }
 }
 return 1;
}

int main() {
 A *a = new A(1, "hello");
 a->show();
 if (a->setname("") == 0) a->show();
 if (a->setname("hello-hello") == 0) a->show();
 delete a;
 cin.sync(); cin.get(); return 0;
}

(код запускался в Visual Studio 2015)

Отметим, что простота эта - кажущаяся и сложность подобной программы очень быстро растёт с усложнением ваших классов.

К тому же, частое освобождение и выделение памяти неизбежно создают "дырки" в оперативке, занятой приложением. Теоретически этого можно избежать, если выделять и освобождать память всегда по принципу стека (последний занявший ресурс объект освобождает его первым), на практике же это почти неосуществимо при разработке сложного приложения.

Альтернативных решений существует несколько - умные указатели, буферы с заранее заданным размером вместо динамической области памяти (не забудьте только, что при любом копировании в буфер нужно контролировать его на переполнение), наконец, использование вместо динамического массива контейнера vector из stl, который проследит за использованием оперативки гораздо лучше, чем это может сделать начинающий программист. Для нашего случая со строкой char * отлично подошёл бы и стандартный тип std::string, с которым программа примет гораздо более простой и понятный вид:

#include <iostream>
#include <string>
using namespace std;

class A {
private:
 int n;
 string s;
public:
 A (int n = 0) { this->n = n; this->s = ""; }
 A (int n, string s) {
  this->n = n;
  this->s = "";
  this->setname(s);
 }
 int setname (string s) {
  if (s.empty()) { this->s = ""; return 1; }
  this->s = s;
  return 0;
 }
 void show() { 
  cout << endl << this->n << ", " << (this->s.size()==0?"(null)":this->s);
 }
};

int main() {
 A a(1, "hello");
 a.show();
 a.setname("");
 a.show();
 a.setname("hello-hello");
 a.show();
 cin.sync(); cin.get(); return 0;
}

21.02.2017, 13:59 [3290 просмотров]


теги: c++ программирование studio

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