Софтуерно Инженерство
Loading...
Pavelgg avatar Pavelgg 1 Точки

operator overloading "operator+()" за конкатенация - C++

Здравейте,

 

Изпитвам затруднение да открия начин да конкатенирам два char масива от два обекта от един тип.

Масивите са с динамично заделяне на паметта.

Компилаторът ми е VS 2013 и използвам strcpy_s(dist_char,strlen(char)+1,char)  и strcat_s(dist_char,strlen(char)+1,char).

//-------------------------------------------

#include"stdafx.h"
#include<iostream>
#include<cstring>
#include<conio.h>
using namespace std;

char buf[50];

class test{
public:
    test();
    test(char *ch);
    ~test();
    test(const test &obj); 
    test& operator=(test &obj);
     test &operator+(test &obj);

    void show(){
        cout <<"Show function : "<< str << endl;
    }
private:
    char *str;
    int len;
};
test::test(){
    cout << "\nNormal construction \n";
    len = 1;
    str = new char[1]{'\0'};
    cout << "Normal construction : " << str << endl;
}
test::test(char *ch){
    cout << "\nConstruction with string\n";
    len = strlen(ch);
    str = new char[len + 1];
    strcpy_s(str, len + 1, ch);
    cout << "Construction with string : " << str << endl;
}
test::~test(){
    cout << "\nDestruction\n";
    delete[] str;
}
test::test(const test&obj){
    cout << "\nCopy construction\n";
    this->len = obj.len;
    delete[] this->str;
    str = new char[this->len + 1];
    strcpy_s(str, this->len + 1, obj.str);
    cout << "Copy construction : " << str << endl;
}

test &test::operator+(test &obj){
    
    delete[] str;
    len = strlen(str) + strlen(obj.str)+1;
    str = new char[len];
    strcat_s(str, len + 1, obj.str);

    return *this;
}

test &test::operator=(test &obj){
    if (this->len < obj.len){
        delete[] this->str;
        this->len = obj.len;
        str = new char[len+1];
    }
    strcpy_s(str, this->len + 1, obj.str);
    cout << "Construction for getting : " << str << endl;

    return *this;
}


int main(){

    test A, B("Ime");
    A.show();
    B.show();

    test C(B);
    C.show();
    test D("Familia");
    D.show();

    B = D;
    B.show();

     A + B;
    A.show();

    _getch();
    return 0;
}

//---------------------------------------------

Грешката , която ми генерира е : Expression: (L"String is not null terminated" && 0)

 

Някой би ли ми дал упътване , как да реша проблема?

Благодаря предварително.

 

Тагове:
0
C++ Programming
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

Здравей,

Доста неща не ми харесват в този код, ще започна от конкретната грешка, но ще ти кажа и за другите:

- test &test::operator+(test &obj), който си написал, изобщо не конкатенира два стринга - изтриваш str на обекта и след това добавяш в новозаделена памент str на другия обект. Това прилича повече на copy-assignment отколкото на operator+. Конкретно за тази ситуация проблемът е, че като заделиш новата памет (str = new char[len]), тази нова памет не е занулена. Тоест там имаш произволни стойности. Оттам ти идва грешката, че не е терминиран стринга - нямаш гаранция какво ще се появи в тази памет, и явно в тази ситуация там се появява нещо, което не завършва с '\0'. Предполагам очакваш, че като заделяш нова памет, тя ще бъде инициализирана с нули - ако искаш това да се случи, трябва кодът ти да е str = new char[len]{} - тези скоби накрая карат C++ да инициализира целия масив с подадените стойности, а елементите, за които няма стойности, ще получат нули - тъй като скобите са празни, всички елементи ще са нули. С тази промяна редът A + B и след това A.show() изглежда минават вярно, но след деструкторите изглежда пак има проблем, но не съм го дебъгвал него.

- твоя operator+ както казах не събира два обекта, а изтрива паметта на първия и я замества с копие на тази на втория. Най-вероятно искаш първо да заделиш нова памет, достатъчно голяма да събере и двата, след това да копираш this->str в нея, след това да копираш obj.str в нея. В момента конкатенираш obj.str с памет, в която не си записал стойности, вместо да го конкратенираш с копие на str. Тоест липсва ти стъпката на прехвърляне на str от старата памет в новата

- operator+ изобщо не трябва да се държи така. Когато имаш int a = 1, int b = 2; операцията a + b; не ти променя a да стане 2, нали? Съответно ако направиш int c = a + b; тогава c == 3, пък a си остава 1 и b си остава 2. Тоест очакваното поведение на operator+ е да върне обект, който представлява сумата, не да променя съществуващите обекти. Тоест сигнатурата му трябва да е Test operator+(const Test &obj) const { ... }. Това, което ти си написал прилича повече на operator+= (променяш текущия обект и връщаш референция към него).

- copy constructor-ът ти няма нужда да трие текущите данни - тепърва конструираш обекта, той няма стойности, които да бъдат изтрити (не му се вика default конструктора, ако това си мислиш). Тоест би следвало да прилича на конструктора, който приема char*.

- Не правиш проверка за self-assignment (Test a; a = a;) в copy-assignment-а. В добрия случай, това означава, че ще имаш излишни операции, но в лошия случай може да си изтриеш паметта без да искаш. Не съм ти проследил кода достатъчно добре за да съм сигурен.

- Като цяло не е добра практика да триеш преди да си заделил новата памет, защото заделянето на нова памет може да не сработи и да се озовеш в ситуация, в която обектът ти си е освободил паметта и сочи към памет, която вече не е негова. Това предполага, че някакъв външен код има try-catch клаузи около операциите на твоя обект, които могат да причинят тази грешка - каквито в случая нямаш - но ако ще пишеш подобен код е добре да си създадеш навици да не триеш преди да си заделил памет.

- Ползваш нестандартни операции. strcopy_s, strcat_s и прочие не са в официалния C++ стандарт, а са Microsoft-ски измислици. Това не е задължително да е лошо, но предвид, че операциите от стандартната библиотека ще имат същия ефект за твоя код, защо не ползваш тях? Така ще можеш да си компилираш кода и на други компилатори/платформи, ще ти е по-лесно да търсиш информация за функциите, повече хора ще могат да те съветват за кода и т.н.

- Защо си усложняваш живота със C-String-ове и функциите им? Пишеш на C++, имаш на разположение std::string, който поддържа всичките операции, които извършваш, включително и управление на паметта. Няма смисъл да имплементираш rule of three, ако имаш клас, който вече я върши тази работа достатъчно добре. Единствената смислена причина да не ползваш string е да искаш по-добър performance, но първо - няма да е толкова по-добър, второ - както си написал кода в момента губиш performance от неща като strlen (то намира дължината като пуска for цикъл и брои, докато не стигне до '\0'). Може би просто искаш да упражниш rule of three, но в този случай най-добре се отърви от C-String-овете и работи с прости char масиви плюс една променлива, която ти пази дължината, и напиши с цикли операциите по конкатенация и прочие - това е доста по-добро упражнение на тези неща, а и най-вероятно ще работи по-бързо.

Поздрави,

Жоро

2
Pavelgg avatar Pavelgg 1 Точки

Благодаря за отговора и за градивните забележки.

Самоук съм в тази област и най-вероятно заради това имам толкова пропуски.

Използвам strcat_s и strcpy_s защото компилатора ми генерира постоянно грешка : _CRT_SECURE_NO_WARNINGS, след отговора се порових из нета и открих как да променя компилатора да не я генерира и да ми компилира правилно strcat и strcpy ,но се получава само за  текущият проект.

Четейки от един учебник стигнах до тази задача където се използва rule of three и strcpy() , strcat().

Благодаря още веднъж за отговора .

0