1

Тема: Віртуальний оператор +

В мене є завдання створити поліморфічний кластер на основі віртуального оператора + і продемонструвати пізнє зв'язування, але я не можу ніде знайти як це правильно робиться. Зі звичайними методами я б справився, але тут через використання оператора все складніше, плюс проблема в різних типах параметрів. Я пробував багато варіантів, але в мене нічого не виходь. Сподіваюся тут мені домоможуть бо я не розумію як це зробити. Ось посилання на Git:

https://github.com/Vladyslavvvvv/Kursova-C-

2

Re: Віртуальний оператор +

Добридень, вітаю на форумі.
Узагалі перевантаження операторів придумали для АТД, а поліморфізм - це з ООП; але C++ таке допускає. Треба тільки юзкейс придумати для такої ситуації.
Тобто є два типи чисел: базовий Base і нащадок Derived. В обох є віртуальний метод

virtual Base operator + (Base&) //у Base
Base operator + (Base&) override //у Derived

А тепер задаємо функцію, що приймає два Base& і повертає Base:

Base polymorphic_call(Base &left, Base &right) {
    return left + right;
}

Власне, все. Ця функція буде вимушена виконати пізнє зв'язування, бо не знає, що саме прийде параметрами.
Тест: https://ideone.com/OQPhDB

Додаткова проблема: ми не можемо напряму повернути Derived, бо тип, що повертається, визначений як Base. А щоб повертати Base& (який допускає повернення Derived), треба придумати, хто володітиме об'єктом, що повертається. Можна хіба що std::shared_ptr<Base> повертати, якщо треба, щоб поверталися різні типи; а це погано для операторів, бо неможливо складні вирази читати. Ну, або ж створювати якісь внутрішні сховища, і ще збирач сміття... коротше, забагато мороки задля такого.

Подякували: Tarpan87, leofun012

3

Re: Віртуальний оператор +

У мене все одно не виходить перевантажити оператор який працює з об'єктом який представляє масив символів так щоб він працював з об'єктом який представляє масив цифр:

// Перевизначення операції +
String String::operator+(String& other) {
    int newLength = getTrueSize() + other.getTrueSize() + 1; // +1 для нуль-термінатора
    char* newStr = new char[newLength];

    copy(str, str + maxLength, newStr); // Копіюємо перший рядок
    copy(other.str, other.str + other.maxLength, newStr + getTrueSize()); // Додаємо другий рядок

    return String(newStr, newLength);
}
// Оператор додавання
BigInt BigInt::operator+(BigInt& other) {
    BigInt result;

    long long res1 = arrayToNumber(digits, size);
    if (negative) {
        res1 = res1 - (res1 + res1);
    }

    long long res2 = arrayToNumber(other.digits, other.size);
    if (other.negative) {
        res2 = res2 - (res2 + res2);
    }

    long long resultNumber = res1 - res2;
    if (resultNumber < 0) {
        result.negative = true;
        resultNumber = abs(resultNumber);
    }

    int* resultDigits;
    int resultSize;
    numberToArray(resultNumber, resultDigits, resultSize);

    result.size = resultSize;
    result.digits = resultDigits;
    
    return result;
}

Типу мені треба перетворювати об'єкти з символів у числа, а потім на виході числа в символи чи як? Я не знаю чому, але до мене тупо не доходить як це зробити

4

Re: Віртуальний оператор +

Робити бігІнт через стринг - не найкраща ідея бо занадто повільно. на гітхабі достаньо нормальних реалізацій.
навіщо цей велосипед не зрозуміло.
ну і стринги можна і так складати без перезавантаження

5

Re: Віртуальний оператор +

Hores написав:

У мене все одно не виходить перевантажити оператор

Для початку - ви взагалі не те робите.
У вас чітке завдання:

Hores написав:

продемонструвати пізнє зв'язування

Якщо ви перевантажуєте функції (методи, оператори) з різними типами параметрів, то компілятор це бачить і застосовує раннє зв'язування. Єдиний спосіб викликати пізнє (динамічне, часу виконання) зв'язування в C++ - це перевантаження віртуальних функцій, успадкованих від єдиного предка. Тобто вам треба, щоб один з ваших класів успадковувався від іншого, або ж вони обидва успадковувалися від третього (наприклад, абстрактного) класа; і всі оператори обов'язково мусять мати однакову сигнатуру. Ви не можете змусити оператор String String::operator+(String& other) і BigInt BigInt::operator+(BigInt& other) зв'язуватися динамічно, бо вони мають різні сигнатури і компілятор їх розрізнить ще під час компіляції.

Як саме мають ці оператори працювати усередині - питання цікаве, але вам спершу треба розв'язати архітектурне питання: які будуть сигнатури операторів, а вже потім думати про те, як правильно їх реалізовувати. Так, цілком можливо, що неправильні сигнатури унеможливлять реалізацію і доведеться їх перевигадувати; але з різними сигнатурами ви ніколи не виконаєте завдання. А коли ви вигадуватимете спільну сигнатуру, вам доведеться ухвалювати рішення, яким буде тип, що його повертатиме цей оператор, і це підкаже, як саме писати оператори.

Можливо, вам варто потренуватися перевантажувати звичайні функції. Або хоча б перечитати підручник (чи відео переглянути, як ви там зараз навчаєтеся).

ur_naz написав:

навіщо цей велосипед не зрозуміло.

Звісно, що не зрозуміло. Я вам уже скільки разів на це відповідав: коли люди навчаються, вони роблять те, що інші вже вміють, саме для того, щоб навчитися. Але ви, схоже, вчитися не вмієте взагалі, інакше б уже зрозуміли.

ur_naz написав:

стринги можна і так складати без перезавантаження

І як же, не розкажете? Навіть у стандартного std::string оператор додавання - не повірите - перевантажений, а у цього самописного й поготів.

Прихований текст

я зроблю вигляд, що не помітив, що ви не розрізняєте перевантаження і перезавантаження, бо дуже соромно

Подякували: leofun011

6

Re: Віртуальний оператор +

Ще порада: спершу напишіть код, який має використовувати ваші класи і перевантажений оператор, а вже потім - реалізацію цих класів та операторів. Так ви краще зрозумієте, що саме вони мають робити.

7

Re: Віртуальний оператор +

Я оголосив їх як virtual і override в .h файлах і ось така реалізація працює:

// Перевизначення операції +
String String::operator+(String& other) {
    cout << "BASE" << endl;
    int newLength = strlen(str) + strlen(other.str) + 1; // Обчислюємо нову довжину рядка
    char* newStr = new char[newLength + 1]; // +1 для нуль-термінатора

    copy(str, str + strlen(str), newStr); // Копіюємо перший рядок
    copy(other.str, other.str + strlen(other.str) + 1, newStr + strlen(str)); // Додаємо другий рядок

    return String(newStr, newLength); // Повертаємо новий об'єкт String
}
String BigInt::operator+(String& other) {
    cout << "DERIVED" << endl;
    int maxSize = max(getLength(), other.getLength());

    // Конвертація рядка символів в масив цифр
    int* buf1 = new int[maxSize]();
    int* buf2 = new int[maxSize]();
    char* str1 = getString();
    char* str2 = other.getString();
    for (int i = 0; i < getLength(); i++) {
        buf1[maxSize - getLength() + i] = str1[i] - '0'; // Віднімання ASCII-коду '0' для отримання числового значення цифри
    }
    for (int i = 0; i < other.getLength(); i++) {
        buf2[maxSize - other.getLength() + i] = str2[i] - '0';
    }

    // Виконання операції додавання
    int* buf3 = new int[maxSize + 1]();
    int carry = 0;
    for (int i = maxSize - 1; i >= 0; i--) {
        int sum = buf1[i] + buf2[i] + carry;
        buf3[i + 1] = sum % 10; // Зберігаємо менш значущий розряд
        carry = sum / 10; // Визначаємо перенесення
    }
    buf3[0] = carry; // Зберігаємо більш значущий розряд

    // Конвертація масиву цифр у рядок символів
    char* resultStr = new char[maxSize + 2]; // +2 для можливого перенесення та нуль-термінатора
    int resultSize = maxSize + (buf3[0] != 0 ? 1 : 0); // Розмір результату
    for (int i = 0; i < resultSize; i++) {
        resultStr[i] = buf3[i] + '0'; // Додавання ASCII-коду '0' для отримання символу цифри
    }
    resultStr[resultSize] = '\0'; // Нуль-термінатор

    // Очищення пам'яті
    delete[] buf1;
    delete[] buf2;
    delete[] buf3;

    return String(resultStr, resultSize);
}

Але тепер проблема в обчисленні, видає такий результат:
Enter the maximum size -1: 2
Enter the value: 2
String: 2       Max. size: 3    True size: 1


Enter the maximum size -1: 2
Enter the value: 2
String: 2       Max. size: 3    True size: 1


WORK
DERIVED
DERIVED
+ : String: 0)  Max. size: 2    True size: 2

main:

#include "String.h"
#include "BigInt.h"

using namespace std;

String polym(String& left, String& right) {
    cout << "WORK" << endl;
    return left + right;
}

int main() {
    BigInt S1, S2, S3, S4, S5, S6;
    String RES;

    cin >> S1;
    cout << S1 << endl;
    cin >> S2;
    cout << S2 << endl;

    polym(S1, S2);

    RES = S1 + S2;
    //S4 = S1 - S2;
    //bool q = S1 * S2;

    cout << "+ : " << RES << endl;
    //cout << " - : " << S4 << endl;
    //cout << " * : " << q << endl;

    return 0;
}

8

Re: Віртуальний оператор +

Тепер я вже розібрався з цим і зрозумів де була проблема. З перетворенням чар в інт і навпаки для додавання також розібрався. Дякую всім

Подякували: koala1

9

Re: Віртуальний оператор +

я мав на увазі те, що не обов'язково сворювати свій стрінг, якщо є стандартний string
взагалі на це боляче дивитись.

10 Востаннє редагувалося Hores (24.04.2024 00:48:42)

Re: Віртуальний оператор +

Мені самому було боляче з цим бавитися, але так треба було

11

Re: Віртуальний оператор +

Це був місклік з цитатою. Тут бракує редагування повідомлень

12

Re: Віртуальний оператор +

Hores написав:

Це був місклік з цитатою. Тут бракує редагування повідомлень

Тепер має не бракувати.