Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
egor2116
338 / 369 / 42
Регистрация: 20.01.2013
Сообщений: 1,116
#1

Для тех кто имеет опыт по ОПП - C++

01.04.2013, 22:04. Просмотров 576. Ответов 8
Метки нет (Все метки)

Посмотрите на код и выскажете свое мнение, соответствует ли он философии ОПП, если нет то скажите в чем ошибки, как лучше и т.д. . Это двухнаправленный список с возможностью одновременного хранения целого, вещественного и строки.

item.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
 
//Класс представляющий тип хранимого данного
class Item
{
private:
    int whole;
    double real;
    char * str;
public:
        enum check {WL,RL,S} data;
public:
    //Конструкторы
    Item();
    Item(int w);
    Item(double w);
    Item(char * w);
    Item(const Item & it);
    //Деструктор
    ~Item();
    //Методы чтения данных
     int getWhole() const;
     double getReal() const;
     char * getStr() const;
     int getData() const;
    //Перегруженный оператор <<
    friend std::ostream & operator<<(std::ostream & out,const Item & i);
 
};
item.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "item.h"
 
//Определение конструкторов
Item::Item(int w){
whole=w;
real=0.0;
str=(char*)"\0";
data=WL;
}
 
Item::Item(double d){
whole=0;
real=d;
str=(char*)"\0";
data=RL;
}
 
Item::Item(char * s){
whole=0;
real=0.0;
str=s;
data=S;
}
 
Item::Item(const Item &it){
whole=it.getWhole();
real=it.getReal();
str=it.getStr();
data=check(it.getData());
}
 
Item::~Item(){}
 
//Определение методов чтения данных
double Item::getReal() const { return real; }
int Item::getWhole() const { return whole; }
char * Item::getStr() const { return str; }
int Item::getData()const { return data; }
 
//Перегрузка оператора <<
std::ostream & operator <<(std::ostream & out,const Item & i){
    if(i.getData()==i.RL)
        std::cout<<i.getReal();
    else if(i.getData()==i.WL)
        std::cout<<i.getWhole();
    else if(i.getData()==i.S)
        std::cout<<i.getStr();
 
    return out;
}
mylist.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "item.h"
 
//Класс представляющий двунаправленный список
class MyList
{
private:
    //Счетчик колличества элементов списка. Доступ для чтения через метод void getCount().
    int count;
    //Структура представляющая узел списка
    struct node {
        Item * itm; //Тип хранимого данного
        node * next; //Следующий элемент
        node * prev; //Предыдущий элемент
        //Конструктор структуры для её инициализации данными
        node(Item * i,node * f,node * p){
            next=f;
            prev=p;
            itm=i;
        }
        };
private:
    //Только для домашнего использования
        void pushF(Item * i);
        void pushE(Item * i);
public :
    node * end; //Конец списка
    node * first; //Начало списка
public:
    //Методы управления списком
 
    //Добавление узла в начало списка
    void pushFirst(int val);
    void pushFirst(double val);
    void pushFirst(char * val);
    //Добавление узла в конец списка
    void pushEnd(int val);
    void pushEnd(double val);
    void pushEnd(char * val);
    //Выборка указаного узла по порядковому номеру
    Item & selectNode(int val);
    //Очистка всего списка
    void clearList();
    //Удаление указанного узла по порядковому номеру
    bool deleteNode(int val);
 
    //Выведение всего содержимого списка на консоль
    void printList();
    //Получение количества узлов списка
    inline int getCount() { return count; }
 
 
 
public:
    //Конструктор по умолчанию
    MyList();
    //Деструктор
   ~MyList();
};
mylist.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include "mylist.h"
 
//Конструктор по умолчанию
MyList::MyList(): count(0),end(NULL),first(NULL) {}
//Деструктор
MyList::~MyList(){ clearList(); }
 
//Методы управления списком
 
void MyList::pushF(Item * i){
    first=new node(i,first,NULL);
    if(!count)
            end = first;
       node *ptr = first->next;
       if(ptr)
            ptr->prev = first;
 
       count++;
}
 
void MyList::pushE(Item * i){
    end = new node(i, NULL, end);
 
       if (!count)
          first = end;
 
       node *ptmp = end->prev;
       if (ptmp)
          ptmp->next = end;
 
       count++;
}
 
//Добавление узла в начало списка, тип записуемого данного int
void MyList::pushFirst(int val){
    Item * i=new Item(val);
    pushF(i);
}
 
//Добавление узла в начало списка, тип записуемого данного double
void MyList::pushFirst(double val){
    Item * i=new Item(val);
  pushF(i);
}
 
//Добавление узла в начало списка, тип записуемого данного char *
void MyList::pushFirst(char * val){
    Item * i=new Item(val);
   pushF(i);
}
 
//Добавление узла в конец списка, тип записуемого данного int
void MyList::pushEnd(int val){
    Item * i=new Item(val);
    pushE(i);
}
 
//Добавление узла в конец списка, тип записуемого данного double
void MyList::pushEnd(double val){
    Item * i=new Item(val);
 pushE(i);
}
 
//Добавление узла в конец списка, тип записуемого данного char *
void MyList::pushEnd(char * val){
    Item * i=new Item(val);
 pushE(i);
}
 
//Выведение всего содержимого списка на консоль
 void MyList::printList(){
    node * ptmp=first;
    int a=1;
    while(ptmp){
         if(ptmp->itm->getData()==Item::WL)
        std::cout<<"Узел № "<<a<<" Данные: "<<ptmp->itm->getWhole()<<" "<<std::endl;
         else if(ptmp->itm->getData()==Item::RL)
        std::cout<<"Узел № "<<a<<" Данные: "<<ptmp->itm->getReal()<<" "<<std::endl;
         else if(ptmp->itm->getData()==Item::S)
        std::cout<<"Узел № "<<a<<" Данные: "<<ptmp->itm->getStr()<<" "<<std::endl;
        ptmp=ptmp->next;
        a++;
    }
    std::cout<<std::endl;
 
}
 
 //Очистка всего списка
void MyList::clearList(){
    while (first)
         {
              node * ptr = first;
              first = ptr->next;
 
              delete ptr->itm;
              delete ptr;
         }
         end = first;
 
         count = 0;
}
 
//Выборка указаного узла по порядковому номеру
Item &  MyList::selectNode(int val){
 
       int tmp=0;
       node * ptmp = first;
 
       while (ptmp)
       {
            tmp++;
            if (val==tmp)
                 return *(ptmp->itm);
            ptmp = ptmp->next;
       }
}
 
//Удаление указанного узла по порядковому номеру
bool MyList::deleteNode(int val){
    if(!count || !val || val>count+1)
             return false;
 
        int tmp=0;
 
        node * ptmp = first;
 
        while (ptmp)
        {
             tmp++;
             if (val==tmp) {
                  node * ptr_prev = ptmp->prev;
                  node * ptr_next = ptmp->next;
 
                  if(ptr_prev)
                       ptr_prev->next = ptr_next;
                  if(ptr_next)
                       ptr_next->prev = ptr_prev;
 
                   delete ptmp->itm;
                  delete ptmp;
 
                  count--;
                  return true;
             }
             ptmp = ptmp->next;
        }
}
Пример использования
main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "mylist.h"
 
int main()
{
    //Создаем список
    MyList list;
 
    //Помещаем в начало списка значение с типом int
    list.pushFirst(99);
 
    //Продолжаем заполнять в конец списока значениями вещественного типа и строкой
    for(int i=1;i<=10;i++){
        if(i==5)   list.pushEnd((char*)"mylist");
        else         list.pushEnd(double(i*25.2564));
    }
 
     //Продолжаем заполнять в конец списока значением строки
    list.pushEnd((char*)"Hello world");
    //Помещаем в начало списка значение с типом int
    list.pushFirst(321);
    list.pushFirst(0);
    list.pushEnd(0.0);
    list.pushFirst(-45.25);
    list.pushEnd(0.0122);
    //Выводим содержимое всего списка на консоль
    list.printList();
 
    //Удаляем выбраный узел
    int del=2;
    std::cout<<std::endl;
    std::cout<<"Удаление узла №"<<del<<std::endl;
    std::cout<<std::endl;
    list.deleteNode(del);
 
    //Выводим содержимое всего списка на консоль
    list.printList();
 
    //Выборка выбранного узла
    int select=1;
    Item  i(list.selectNode(select));
 
    //Вывод данных выбранного узла на консоль
    if(i.getData()==Item::RL)
    std::cout<<"Выборка узла № "<<select<<" Данные: "<<i.getReal()<<std::endl;
    else if(i.getData()==Item::WL)
    std::cout<<"Выборка узла № "<<select<<" Данные: "<<i.getWhole()<<std::endl;
    else if(i.getData()==Item::S)
    std::cout<<"Выборка узла № "<<select<<" Данные: "<<i.getStr()<<std::endl;
 
    std::cout<<"operator << "<<i<<std::endl;
 
 
    return 0;
}
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
01.04.2013, 22:04     Для тех кто имеет опыт по ОПП
Посмотрите здесь:

C++ Из списка студентов на экран вывести данные о тех, кто учится в группе 664, проживает в общежитии, но родился в городе Ижевске
Записи: Задать список спортсменов, распечатать сведения о тех из них, кто занимается плаваньем C++
C++ Задание по ОПП
Сортировка вектора сложного класса (Просто для тех кто знает) C++
C++ Для тех кто делает оконные приложения в Builder C++ Borland
C++ Для тех кто умеет работать с файлами (Вывести на экран только строки, содержащие двузначные числа)
C++ Программы на ОПП С++. 911
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
ninja2
230 / 186 / 7
Регистрация: 26.09.2012
Сообщений: 2,018
Завершенные тесты: 1
02.04.2013, 02:42     Для тех кто имеет опыт по ОПП #2
Ну как тебе сказать соответсвует. На двух классах. Ну видимо соответсвует. Честно я даже не смотрел как там ты чо делал. Я тоже строил линейный список, поэтому понимаю о чом речь. Я строил класс узел делал другом класс список это чтобы можно было обращаться к закрытым членам. Ну что ну два класса. Ну какой там может быть ООП. Ты просто структурировал программу классами. Хотя кто его знает наверно это и есть ООП. Хз. Я вообще не понимаю что это за понятие философия ООП. Мне вообще тяжело в ООП стиле писать, а классы в большинстве своем я просто использую как набор функций.

Ладно конкретно что касается программы:

Да я честно не спец но классы правильно создал, интерфейс выделил в заголовочный файл, а определение функций в сpp.

Правда минус ты не используешь защиту от повторного включения нужно в заголовочных файлах добавлять всегда #ifndef #define #endif , чтобы не было повторного включения файлов.

Добавлено через 1 минуту
Если ты два раза где, то #include "class.h" втулишь, то без защиты у тебя вылезет ошибка например двойное определение какой нить переменной либо константы и тупо программа не будет компилироваться.
Avazart
7044 / 5221 / 259
Регистрация: 10.12.2010
Сообщений: 22,944
Записей в блоге: 17
02.04.2013, 03:40     Для тех кто имеет опыт по ОПП #3
Ну если говорить об ООП то я вижу тут только инкапсуляцию.
Для "наследования" возможно стоило создать еще абстрактный класс - задающий интерфейс.

Если не ограничиваться рамками ООП, то так сказать для усугубления добавить шаблоны.
egor2116
338 / 369 / 42
Регистрация: 20.01.2013
Сообщений: 1,116
02.04.2013, 09:03  [ТС]     Для тех кто имеет опыт по ОПП #4
Если не ограничиваться рамками ООП, то так сказать для усугубления добавить шаблоны.
Шаблоны нельзя.
Для "наследования" возможно стоило создать еще абстрактный класс - задающий интерфейс.
А можете поподробней объяснить с этого момента.

В общем смысл темы такой, нужно сделать так что бы привести код к ООП, использовать как можно больше преимуществ.
ya_noob
_
201 / 145 / 9
Регистрация: 08.10.2011
Сообщений: 432
02.04.2013, 09:56     Для тех кто имеет опыт по ОПП #5
Как-то нехорошо у вас реализован класс Item, много лишней информации хранит для элементов.
Возможно стоит добавить динамического полимрфизма: создать иерархию классов для хранимых элементов. Например, создать абстрактный класс Item, в котором объявить чисто виртуальные функции для доступа к полям, и "отнаследовать" от него 3 класса для конкретных типов данных (целых, вещественных и строк) и там определить сеттеры и геттеры для своего типа элементов.
_Alexander
12 / 12 / 1
Регистрация: 05.11.2012
Сообщений: 49
02.04.2013, 10:05     Для тех кто имеет опыт по ОПП #6
ИМХО, в двунаправленном списке в классе Iteem должны быть объявлены указатели на предыдущий и последующий элементы
lemegeton
2915 / 1344 / 133
Регистрация: 29.11.2010
Сообщений: 2,720
02.04.2013, 11:03     Для тех кто имеет опыт по ОПП #7
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Цитата Сообщение от egor2116 Посмотреть сообщение
Это двухнаправленный список с возможностью одновременного хранения целого, вещественного и строки.
Лучше сделать двунаправленный список с возможностью хранения указателей, раз уж шаблоны нельзя. Придвется, конечно, постоянно приводить, зато универсальней.

Самосоздающуюся ноду можно сделать еще и самодобавляющейся и самоудаляющейся.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    struct node {
        Item * itm; //Тип хранимого данного
        node * next; //Следующий элемент
        node * prev; //Предыдущий элемент
        //Конструктор структуры для её инициализации данными
        node(Item * i,node * f,node * p){
            next->prev = prev->next = this;
            next=f;
            prev=p;
            itm=i;
        }
        virtual ~node() {
            prev->next = next;
            next->prev = prev;
        }
        };
Одно из больших преимуществ ООП -- конструкторы и деструкторы, неявно вызывающиеся при создании и удалении объектов.
Посмотрите, как реализован связный список. На самодобавляющихся и самоудаляющихся нодах.
Плюс реализация интерфейса к хранимым объектам.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <iostream>
#include <iomanip>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <cmath>
 
// общий интерфейс хранимых объетов (примитивный вариант)
struct Storable {
  enum DataTypes {
    DATATYPE_NONE,
    DATATYPE_CSTRING,
    DATATYPE_INTEGER,
    DATATYPE_DOUBLE
  };
  virtual int getDataType() const { return DATATYPE_NONE; };
  virtual ~Storable() {}
};
 
struct NodeBase {
  NodeBase *prev, *next;
  NodeBase() : prev(this), next(this) {}
  NodeBase(NodeBase *prev, NodeBase *next) : prev(prev), next(next) {
    prev->next = next->prev = this;
  }
  virtual ~NodeBase() {
    prev->next = next;
    next->prev = prev;
  }
};
 
struct Node : public NodeBase {
  void *value;
  Node(NodeBase *prev, NodeBase *next, void *value)
    : NodeBase(prev, next), value(value) {}
};
 
class List {
 public:
  List() : size(0), base() {}
  virtual ~List() { clear(); }
  void pushFront(void *value) {
    new Node(&base, base.next, value);
    ++size;
  }
  void pushBack(void *value) {
    new Node(base.prev, &base, value);
    ++size;
  }
  const void *getFront() const {
    return static_cast<Node*>(base.next)->value;
  }
  const void *getBack() const {
    return static_cast<Node*>(base.next)->value;
  }
  void popFront() {
    // тут нужно предусмотреть проверку на пустоту стека.
    // кинуть исключение или еще что-нибудь сделать
    // если стек пустой
    delete base.next;
    --size;
  }
  void popBack() {
    // тут нужно предусмотреть проверку на пустоту стека.
    // кинуть исключение или еще что-нибудь сделать
    // если стек пустой
    delete base.prev;
    --size;
  }
  bool isEmpty() {
    return base.next == &base;
  }
  const size_t getSize() const { return size; }
  void clear() {
    while (!isEmpty()) {
      popBack();
    }
  }
 private:
  size_t size;
  NodeBase base;
};
 
class StorableString : public Storable {
 public:
  StorableString(const char *data)
    : data(strcpy(new char[strlen(data) + 1], data)) {}
  StorableString(const StorableString &other)
    : data(strcpy(new char[strlen(other.data) + 1], other.data)) {}
  const char *getCString() { return data; }
  virtual int getDataType() const { return DATATYPE_CSTRING; };
  virtual ~StorableString() {
    delete data;
  }
 private:
  const char *data;
};
 
class StorableInteger : public Storable {
 public:
  StorableInteger(int data) : data(data) {}
  const int getInteger() { return data; }
  virtual int getDataType() const { return DATATYPE_INTEGER; };
 private:
  int data;
};
 
class StorableDouble : public Storable {
 public:
  StorableDouble(double data) : data(data) {}
  const int getDouble() { return data; }
  virtual int getDataType() const { return DATATYPE_DOUBLE; };
 private:
  double data;
};
 
int main(int argc, char *argv[]) {
  srand(time(0));
 
  List a;  
  a.pushBack(new StorableString("Text"));
  a.pushBack(new StorableDouble(1.25));
  a.pushBack(new StorableInteger(2));
 
  while (!a.isEmpty()) {
    switch (((Storable*)a.getFront())->getDataType()) {
      case Storable::DATATYPE_CSTRING:
        std::cout << ((StorableString*)a.getFront())->getCString() << std::endl;
        break;
      case Storable::DATATYPE_INTEGER:
        std::cout << ((StorableInteger*)a.getFront())->getInteger() << std::endl;
        break;
      case Storable::DATATYPE_DOUBLE:
        std::cout << ((StorableDouble*)a.getFront())->getDouble() << std::endl;
        break;
      default:
        std::cout << "Unrecognized datatype." << ((Storable*)a.getFront())->getDataType() << std::endl;
    }
    delete (Storable*)a.getFront();
    a.popFront();
  }
 
  std::cin.get();
  return 0;
}
Добавлено через 42 минуты
Естественно, в реальной жизни вместо switch должна быть диспетчеризация или паттерн типа visitor.
ForEveR
Модератор
Эксперт С++
7958 / 4720 / 319
Регистрация: 24.06.2010
Сообщений: 10,525
Завершенные тесты: 3
02.04.2013, 11:08     Для тех кто имеет опыт по ОПП #8
lemegeton, Один вопрос. Почему храним void* вместо Storable*?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
02.04.2013, 11:22     Для тех кто имеет опыт по ОПП
Еще ссылки по теме:

Найти сумму тех из элементов a2j(j=1,...,m), для которых a1j имеет значение наибольшего среди значений a11, a12,...,a1m C++
C++ Найти сумму тех из элементов a2j(j=1,...,m), для которых a1j имеет значение наибольшего среди значений
C++ Из списка спортсменов выбрать тех кто занимается плаванием
Процедуры в С++ (для тех, кто знает и Pascal, и C++) C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
lemegeton
2915 / 1344 / 133
Регистрация: 29.11.2010
Сообщений: 2,720
02.04.2013, 11:22     Для тех кто имеет опыт по ОПП #9
Цитата Сообщение от ForEveR Посмотреть сообщение
lemegeton, Один вопрос. Почему храним void* вместо Storable*?
Тут есть про и консы, но на мой взгляд, сущность, хранящая абстрактный указатель универсальнее (orthogonal), чем сущность, хранящая типизированный указатель. В oldschool-С и седой древности, когда шаблонов еще не было, считалось, что надо делать так.

Конечно, этот шаг вызван ограничением на использование шаблонов.
Yandex
Объявления
02.04.2013, 11:22     Для тех кто имеет опыт по ОПП
Ответ Создать тему
Опции темы

Текущее время: 11:46. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru