C ++ глубокое копирование const char * внутри массива классов? - PullRequest
1 голос
/ 06 апреля 2020

Так что у меня проблема, я делаю уроки. В этом классе я должен иметь массив некоторых данных. У меня нет проблем с назначением и созданием новых данных, например, с x2.NewAccount ("123456", 1000); это работает нормально, проблема в том, что я пытаюсь создать данные со строкой, которая адресована некоторой переменной. Я знаю кое-что о глубоком копировании, но не знаю, как запрограммировать = оператор в моем случае + я подумал, что strcpy, но это не работает, а.

PS: Это школьная программа, поэтому, пожалуйста, не судите меня за то, что я не использую заголовки и кучу включений, которые я не использую в коде. Это сделано моей школой, и я не могу изменить их + добавить их (я знаю, что со строкой из c ++ это было бы намного проще). Спасибо за любую помощь.

#ifndef __PROGTEST__
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <cctype>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
#endif /* __PROGTEST__ */

struct data_history{
    int money = 0;
    bool Income;
    const char * UnStr;
    const char * to_from;
};

struct client{
    const char * accID;
    int Balance;
    int def_bal;
    data_history * history;
    int in_index = 0;
    int in_cap = 10;

    friend ostream &operator << (ostream &output , client p){
        output << p.accID << ":" << endl << "   " << p.def_bal << endl;
        for (int i = 0 ; i < p.in_index ; i++){
            if (p.history[i].Income == false)
                output << " - " << abs(p.history[i].money) << ", to: " << p.history[i].to_from << ", sign: " << p.history[i].UnStr << endl;
            else
                output << " + " <<abs(p.history[i].money) << ", from: " << p.history[i].to_from << ", sign: " << p.history[i].UnStr << endl;
        }
        output << " = " << p.Balance << endl;
        return output;
    }
};

class CBank
{
public:
    int cap = 10;
    int index = 0;
    client * database;
    ~CBank(){
        for (int i = 0 ; i < index ; i++)
                delete[] database[i].history;
        delete[]database;
    }
    CBank(){
        database = new client[cap];
    }
    bool NewAccount ( const char * accID, int initialBalance ){
        for(int i = 0 ; i < index ; i ++)
            if (accID == database[i].accID) {
                return false;
            }
        //strcpy (database[index].accID , accID );  // Im getting errors while compileing (cuz I was using const char * for database.accID when i chenged it i got program crash. 
        database[index].accID = accID;
        database[index].Balance = initialBalance;
        database[index].def_bal = initialBalance;
        database[index].in_cap = 10;
        database[index].history = new data_history[database[index].in_cap];
        index ++;
        return true;
    }
    client Account (const char * accID ){
        const char * input  =accID;
        for (int i = 0 ; i < index ; i++){
            if (database[i].accID == input )
                return database[i];
        }
        throw "error";
    }
    void print (){
        for (int i = 0 ; i  < index ; i ++) {
            cout << endl;
            cout << i << " = "<< " ID = " << database[i].accID << " | Balance = " << database[i].Balance << endl;
            cout << "===Account history ===\n\n";
            for (int y = 0 ; y < database[i].in_index; y++) {
                cout << "Was it for him? : " << boolalpha << database[i].history[y].Income
                    << "\nHow much : " << database[i].history[y].money << "\nUnique string : "
                    << database[i].history[y].UnStr << "\nfrom/to: " << database[i].history[y].to_from << endl << endl;
            }
        }
    }

private:

};


#ifndef __PROGTEST__
int main ( void )
{
    char accCpy[100], debCpy[100], credCpy[100], signCpy[100];
    CBank x2;
    strncpy ( accCpy, "123456", sizeof ( accCpy ) );
    assert ( x2 . NewAccount ( accCpy, 1000 ) );
    x2 . print();
    cout << "\n\n\n\n";
    strncpy ( accCpy, "987654", sizeof ( accCpy ) );
    assert ( x2 . NewAccount ( "987654", -500 ) );
    x2 . print();
}
#endif /* __PROGTEST__ */

1 Ответ

0 голосов
/ 09 апреля 2020

При использовании database[index].accID = accID; вы делаете только поверхностную копию (и полагаетесь на то, что вызывающая сторона сохраняет эту память действительной, так как указатель может быть доступен).

Вы правильно определили, что вам нужно предварительно сформировать глубокое копирование, но client::accID - это просто указатель, но вы не можете копировать в него, пока не инициализируете его, чтобы указать на некоторую память.

Один из способов сделать это - динамически распределять и управлять client::accID, аналогично тому, как вы динамически распределяете и управляете client::history.

Вместо strcpy (database[index].accID , accID ); или database[index].accID = accID;, попробуйте:

size_t bufsize = strlen(accID) + 1;
char *buf = new char[bufsize];
memcpy(buf, accID, bufsize);
database[index].accID = buf;

и в деструкторе добавить:

delete[] database[i].accID

Как уже отмечали другие, этот стиль программирования на C ++ очень подвержен ошибкам и плохо рассматривается сообществом. Этого ручного управления памятью можно легко избежать с помощью стандартных библиотечных классов. Даже после внесения изменений, указанных выше, ваша программа будет работать неопределенно, если вы начнете копировать CBank объекты.

Даже если вам не нужно выполнять это задание, попробуйте переписать это как упражнение с:

  • std::string вместо C -строки для accID;
  • std::vector вместо data_history массива

Также к вашему сведению:

if (database[i].accID == input )

Не будет делать то, что ожидается с c -струны ...

...