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
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.