1

Тема: Стек, що містить різні типи.

У мене проблема:
Треба створити стек, який працював би з різними стандартними типами.
Моє рішення:
дописати двух child'ів, які мають одне поле відповідного типу, є дружніми у відношенні до класу Stack(щоб все, що належить класам Char та Float, було 'private').
створити масив вказівників на Stack у цьому ж класі.
коли треба додати елемент, проблем не виникає: просто перевантажуємо функцію push за аргументами. Одна робить потрібний елемент массиву типу Stack вказівником на тип Float/Char(бо вони нащадки). Інша - на Char/Float.
я створив віртуальну ффункцію pop. Одноіменна є також і в флоата/чара. Ці функції повертають значення типу флоат та чар відповідно. Отже, судячи з усього, нам навіть не важливо, що робить функція pop у базовому класі, оскільки вона має "пропускатися". Ношотонето.
Ось код:

class Stack {
protected:
    static const int SZ = 100;
    Stack* mas[100];
    int top;
public:
    Stack(): top(0) {  }
    void push(float);
    void push(char);
    virtual float pop() { return mas[top--]->pop(); }
    int gettop() { return top; }
};

class Float:public Stack { float numb; friend Stack; Float(float x): numb(x) {  } float pop() { return numb; } };
class Char:public Stack { char symb; friend Stack; Char(char s): symb(s) {  } float pop() { return symb; } };

void Stack::push(float n) { mas[top++] = new Float(n); }
void Stack::push(char s) { mas[top++] = new Char(s); }

2 Востаннє редагувалося koala (16.07.2013 12:51:01)

Re: Стек, що містить різні типи.

Я можу багато чого сказати з приводу такого рішення (і мало хорошого), але найголовніше: чи воно працює так, як ви очікували? Якщо так, то в чому питання? Якщо ні, то в чому саме проблема?

3

Re: Стек, що містить різні типи.

ні. не працює. компілюється нормально, але в процесі виникає помилка.
а що жахливого в моєму коді?

4

Re: Стек, що містить різні типи.

Явнонете.

Стек - це коробка. Нащо вам масив бірок на коробки у  такій же коробці?
І ви розв'язуєте не поставлену задачу:

Треба створити стек, який працював би з різними стандартними типами

С++ - статично типізована мова, тож не так просто тут розмістити різнотипні дані. Варіанти:
1. Проблемний: queue<void*> mas;
2. Почитати код STL-ного класу stack.
3. Повне ошукання користувача - фасад: class Int, Float, etc (приблизно ваша ідея, але успадкований від чогось типу DataType і з повним перевантаженням усіх можливих операторів) із поліморфізмом.

5

Re: Стек, що містить різні типи.

це лише частина завдання. Задача тематична. с++ вивчаю сам, тому досвіду мало. Взагалі, треба розібрати вираз. А стек - це лише допоміжна структура.

6

Re: Стек, що містить різні типи.

Так, і ще є
std::stack< boost::variant<...> >

7

Re: Стек, що містить різні типи.

але чому, таки, є помилка?

8

Re: Стек, що містить різні типи.

incred написав:

але чому, таки, є помилка?

Ви проігнорували зауваження:

Стек - це коробка. Нащо вам масив бірок на коробки у  такій же коробці?

Стек має містити елементи, як банка. Він не є елементом-у-собі.

9

Re: Стек, що містить різні типи.

тобто це умвно-заборонено(це стосується й інших структур)

10

Re: Стек, що містить різні типи.

incred написав:

тобто це умвно-заборонено(це стосується й інших структур)

Ви зараз про що?

11

Re: Стек, що містить різні типи.

до речі, массив заданий без будь-яких намірів створювати об'єкти типу Stack - це все для віртуальної функції

12 Востаннє редагувалося koala (18.07.2013 10:30:34)

Re: Стек, що містить різні типи.

Добре, тоді таки розберу...

static const int SZ = 100;

Статичний розмір - погано, та не смертельно.

Stack* mas[100];

Стек складається з... вказівників на стеки? Кожен з яких теж складається з вказівників на стеки? Неочевидне рішення. Геморой гарантовано.

class Float:public Stack { float numb; friend Stack; Float(float x): numb(x) {  } float pop() { return numb; } };

Цечитаєтьсяприблизнонастількижзручнояківашкод. Нові рядки ставте.
Float - це такий стек? Тобто Float містить 100 вказівників на інші стеки? Наслідування - це свіввідношення "є". A:public B означає "A - це такий B".
friend. Дружні функції. Цікава і корисна фішка C++, користуватися якою не можна. Чому? Для цього розберемо, звідки вона виникла.
C++ - мова мультипарадигмальна. Тобто нею можна писати імеративні програми, функціональні, декларативні, ОО і багато чого іншого. Проблема починається, коли в нас є функціональна бібліотека, яку треба підкрутити на ООП-програму. Програміст бібліотеки гадки не мав, що йому доведеться стикатися із обмеженнями доступу, а переписувати те все немає часу, сил, грошей програмісту і т.д. Що робити? Ставити френдів. В інших випадках френдів слід уникати, бо це хак, викликаний саме мультипарадигмальністю мови. НМД, звісно.
Функція pop у Float - невіртуальна. Це баг чи фіча?

І пам'ять тече - new є, а delete'ів катма.
Так, і ще класи Char і Float - близнюки. Треба їх або одним класом робити, або шаблоном (template). Дві сутності з принципово однаковим кодом існувати не можуть, принцип DRY ніхто не скасовував.

Подякували: Очі.завидющі, incred3

13

Re: Стек, що містить різні типи.

*WALL* видаліть цю тему, будь ласка

14

Re: Стек, що містить різні типи.

incred написав:

*WALL* видаліть цю тему, будь ласка

Щоб наступний такий, як ви, знову її створив?
Краще напишіть робочий код і викладіть сюди.

15

Re: Стек, що містить різні типи.

ну, то це трохи папізже...

16 Востаннє редагувалося incred (18.07.2013 08:11:48)

Re: Стек, що містить різні типи.

Є!!
Стек обов'язково містить масив. Тут масив має містити елементи різних типів. Це можна організувати, створивши 3 класи - базовий, який і створить масив, та 2(в нашому випадку) похідних, які заповнять масив.
(масив буде масивом вказівників на тип базового)
Метод Stack::push()  перевантажений - це превантаження і визначатиме, під об'єкт якого з 2 класів треба виділити пам'ять.
Одним великим недоліком цього рішення є те, що Stack::pop() повертає вказівник на потрібну інформацію. Це дуже незручно. Після видалення об'єкту класу Stack ми втрачаємо доступ до інформації, якщо на неї зсилався який-небудь вказівник.
Ось мій код:

class Base {                //клас, що використаємо для створення "різнотипового" масиву
public:
    virtual operator float()=0;    //Stack::pop() повертає Base* - перетворення - "одне з" особистих бажань 
    virtual operator char()=0;    //класу(до алгоритму не відноситься, бо, можливо, мені колись треба буде
};                        //повернути інформацію у типі, н-д, Garden або Ceiling).

class Char:public Base {    //якийсь клас із великою кількістю полів та методів
    char symb;
public:
    Char(char s): symb(s) {  }
    operator char() { return symb; }    //це могла б бути будь-яка інша функція(не акцентую увагу
                            //на тому, чому тут саме ця функція)
    operator float() { return 0; }        //до бальби, а компілятор лихословить
};

class Float: public Base {    //ще один складний клас, пов'язаний із попереднім лише тим,
                    //що входитиме до складу масиву
    float numb;
public:
    Float(float n):numb(n) {  }
    operator float() { return numb; }
    operator char() { return 0; }
};

class Stack {
    Base* mas[100];
    int top, maxTop;        //maxTop вказує на кількість взагалі створених елементів(вона може відрізнятися від
public:                //поточної)
    Stack(): top(0), maxTop(0) {  }
    ~Stack() {
        for(int i = 0; i < maxTop; i++)
            delete mas[i];
    }
    void push(char s) { 
        mas[top++] = new Char(s);
        if(top >= maxTop) maxTop = top;
    }
    void push(float n) {
        mas[top++] = new Float(n);
        if(top >= maxTop) maxTop = top;
    }
    Base* pop() {
        return mas[--top];
    }
    int getTop() { return top; }
};
Подякували: Replace, koala2

17 Востаннє редагувалося incred (08.09.2013 15:00:41)

Re: Стек, що містить різні типи.

кому цікаво, - розбір виразу:

Прихований текст
class expression {
    Stack stack;
    char* str;
    const int LEN;
    float toNumber(int&);
public:
    expression(char* s): LEN(strlen(s)) { str = s; }
    void parse();
    float solve();
};

float expression::toNumber(int& i) {
    int inc = 1; float temp = 0;
    for(int j = 0; (*(str+i) >= '0' && *(str+i) <= '9') || *(str+i) == '.'; j += inc, i++) {
        if(*(str+i) != '.') {
            temp *= (j >= 0 ? pow(10.0, j) : 1.0);
            temp += (int(*(str+i)) - 48) * (j >= 0 ? 1.0 : pow(10.0, j));
        }
        else { j = 0; inc = -1; }
    } i--; return temp;
}

void expression::parse() {
    char tempC; float tempF;        //temp float and char
    for(int i = 0; i < LEN; i++) {
        if(*(str+i) >= '0' && *(str+i) <= '9')
            stack.push(toNumber(i));
        else if(*(str+i) == '+' || *(str+i) == '-' || *(str+i) == '*' || *(str+i) == '/') {
            if(stack.getTop() != 1) {
                tempF = *stack.pop();
                tempC = *stack.pop();
                if(tempC == '*' || tempC == '/')
                    stack.push(float(*stack.pop()) * (tempC == '*' ? tempF : 1/tempF));
                else { stack.push(tempC); stack.push(tempF); }
            }
            stack.push(*(str+i));
        }
    }
}

float expression::solve() {
    float temp;
    while(stack.getTop() != 1) {
        temp = float(*stack.pop());
        switch(char(*stack.pop())) {
        case '+': stack.push(float(*stack.pop()) + temp); break;
        case '-': stack.push(float(*stack.pop()) - temp); break;
        case '*': stack.push(float(*stack.pop()) * temp); break;
        case '/': stack.push(float(*stack.pop()) / temp); break;
        }
    }
    return *stack.pop();
}
Подякували: Replace1

18 Востаннє редагувалося koala (18.07.2013 14:31:33)

Re: Стек, що містить різні типи.

По стеку: непогане рішення на чистому ООП. Щоправда, пам'ять все одно може текти - знищення об'єктів покладається на функцію, що викликає pop (власне, і тече).
Втім, я б тут користувався union-ами і не заморочувався :)
По виразах:
- немає роботи із некоректним вводом;
- *(str+i) значно краще виглядає як str[ i]; порівняйте

*(str+i) >= '0' && *(str+i) <= '9'

зі

isdigit(str[i])

, а

*(str+i) == '+' || *(str+i) == '-' || *(str+i) == '*' || *(str+i) == '/'

зі

strchr("-+*/", str[i])

- змінна LEN очевидно має бути проголошена в parse, а ініцалізувати str треба поза кодом, але це вже дрібниці;
- все ж краще б в стеку були дві функції - popfloat і popchar. Перетворення типів - це те, що C++ вміє найкраще і чого слід максимально уникати :), саме тому такі незручні pure-C++ касти, щоб постійно про це нагадувати.
А, іще замість "магічної константи" 48 писати '0'. Ну і перетворення на int зайве, char - теж число. Не

int(*(str+i)) - 48

а

str[i]-'0'
Подякували: Replace, incred2

19

Re: Стек, що містить різні типи.

koala написав:

- все ж краще б в стеку були дві функції - popfloat і popchar.

Я всіляко намагався уникнути цього.
По-перше, наскільки я знаю, таких методів стандартний клас не має.
По-друге, я наголошував на тому, що класи Char та Float мають "дуже" складну будову. Вони можуть бути слабо пов'язані один з одним, мати велику кількість полів, які треба б повернути всі одразу - єдиний вихід: повертати цілий об'єкт. Також, в тому випадку якщо клас розпізнаватиме 20 різних типів, було б досить незручно створювати 20 функцій у класі Stack(звичайно, це можна зробити в тих же 20 класах, якщо потрібно).

20 Востаннє редагувалося koala (18.07.2013 22:41:39)

Re: Стек, що містить різні типи.

incred написав:
koala написав:

- все ж краще б в стеку були дві функції - popfloat і popchar.

Я всіляко намагався уникнути цього.
По-перше, наскільки я знаю, таких методів стандартний клас не має.

Тому стандартний клас і не годиться.

incred написав:

По-друге, я наголошував на тому, що класи Char та Float мають "дуже" складну будову. Вони можуть бути слабо пов'язані один з одним, мати велику кількість полів, які треба б повернути всі одразу - єдиний вихід: повертати цілий об'єкт. Також, в тому випадку якщо клас розпізнаватиме 20 різних типів, було б досить незручно створювати 20 функцій у класі Stack(звичайно, це можна зробити в тих же 20 класах, якщо потрібно).

А яка різниця? У вас все одно буде 20 функцій-перетворювань в класі Stack, причому вони вочевидь нестійкі до помилок (якщо спробувати перетворити Float на char, буде помилка через повернення 0, і цю помилку важко виявити). І взагалі, тут в будь-якому разі час до шаблонів звертатися. Щось типу (дуже приблизно)

template<typename T>
T Stack::pop()
{
  if(typeid(numb)==typeid(T))
  {
    top--;
    return numb;
  }
  //і тут обробляємо помилку
}
Подякували: incred1