Problem z konstruktorem kopiującym

0

Przy wywołaniu konstruktora kopiującego program VS 2017 daje wyjątek new_scalar.cpp, ale kod jest z wykładu i mam go przerobić na kolejne zajęcia. Jak to zrobić skoro kod źródłowy nie działa? Destruktor dodałam sama, w metodzie setmail const przy char jest chwilowo do podanych danych

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
using namespace std;

class MojaKlasa {
	char** email;
	int ile;

public:
	MojaKlasa(int x);
	MojaKlasa(MojaKlasa& mk);    // konstruktor kopiujący
	~MojaKlasa(); // destruktor
	void setemail(const char* adres, int idx) { strcpy(email[idx], adres); }
	char* getemail(int idx) { return email[idx]; }
};

MojaKlasa::MojaKlasa(int x) {
	email = new char*[ile = x];
	for (int i = 0; i < ile; i++) email[i] = new char[100];
}

MojaKlasa::MojaKlasa(MojaKlasa& mk) {
	email = new char*[mk.ile];
	ile = mk.ile;
	for (int i = 0; i < ile; i++) {
		email[i] = new char[100];
		strcpy(email[i], mk.email[i]);
	}
}

MojaKlasa::~MojaKlasa() {
	for (int i = 0; i < ile; i++) {
		delete[] email[i];
	}
	delete[] email;
}

int main() {
	MojaKlasa MK(3);
	MK.setemail("[email protected]", 0);
	MojaKlasa MK2(MK);
	printf("%s\n", MK2.getemail(0));
	MK.setemail("[email protected]", 0);
	printf("%s\n", MK2.getemail(0));
	return 0;
}
0

Wszystko masz dobrze, w linii 44 masz literówkę. Bo działasz na obiekcie MK, a powinnaś na MK2 czyli powinno być tak

int main() {
	MojaKlasa MK(3);
	MK.setemail("[email protected]", 0);
	MojaKlasa MK2(MK);
	printf("%s\n", MK2.getemail(0));
	MK2.setemail("[email protected]", 0); // <-- w tej linii była literówka
	printf("%s\n", MK2.getemail(0));
	return 0;
}
0

Z typowych bledow w tym kodzie, z ktorych calkiem prawdopodobne ze prowadzacy zajecia nie zdaje sobie sobie sprawy, to:

  • sa mozliwe wycieki pamieci
  • moze sie wysypac jesli dlugosc emaila bedzie wieksza niz programista sobie zalozyl (nie jest to jedyne miejsce gdzie moze sie wysypac, ale powiedzmy ze pozostale da sie usprawiedliwic)

A wystarczy zmienic char** email na std::vector<std::string> zeby te problemy rozwiazac. I przy okazji mocno uproscic kod. I przy okazji poprawny move konstruktor sie wygeneruje.

1

Ja bym radził zmienić książkę, bo twój kod używa wzorców, które miały sens przed 1994r.
Wiele książek (w zasadzie kopiując) pisane jest przez ludzi, którzy piszą je na podstawie tego z czego sami się uczyli.

Generalnie twój kod zawiera z dużo używania C.

Narzędzie Address Sanitizer wykrywa błędy: https://godbolt.org/z/KdKE5vhKs
Wystraszy używać standardowej biblioteki C++ a nie C i wszystko się samo naprawia:

#include <iostream>
#include <string>
#include <string_view>
#include <vector>

class MojaKlasa {
    std::vector<std::string> emails;

public:
    MojaKlasa(int x);
    MojaKlasa(MojaKlasa& mk);
    ~MojaKlasa();
    void setemail(std::string_view adres, int idx);
    std::string_view getemail(int idx);
};

MojaKlasa::MojaKlasa(int x)
    : emails(x)
{
}

MojaKlasa::MojaKlasa(MojaKlasa& mk) = default;

MojaKlasa::~MojaKlasa() = default;

void MojaKlasa::setemail(std::string_view adres, int idx)
{
    emails.at(idx) = adres;
}

std::string_view MojaKlasa::getemail(int idx)
{
    return emails.at(idx);
}

int main()
{
    MojaKlasa MK(3);
    MK.setemail("[email protected]", 0);
    MojaKlasa MK2(MK);
    std::cout << MK2.getemail(0) << '\n';
    MK2.setemail("[email protected]", 0);
    std::cout << MK2.getemail(0) << '\n';

    return 0;
}

https://godbolt.org/z/Y89ohGqf5


W twoim programie problemem jest brak inicjalizacji zawartości elementów tablicy. Pow ykonaniu konstruktora: MojaKlasa::MojaKlasa(MojaKlasa& mk) zmienna email jest prawidłową tablicą, ale zawiera śmieci pamięci. email[0], email[1], email[2] zawierają losowe wartości.

Następnie wywołujesz prawidłowy konstruktor kopiujący i ta linijka:

strcpy(email[i], mk.email[i]);

Próbuje skopiować napis. Jako, że napis w mk.email[i] ma niezdefiniowaną wartoćć (zawiera śmieci), a strcpy kopiuje dane az do napotkania zera, to zależnie od szczęścia/pecha twój program, może wydawać się działać lub będzie miał crash-a.

Wystarczy poprawić tak, by nie było śmieci w tablicy:

for (int i = 0; i < ile; i++) email[i] = new char[100]{};

i działa jak należy: https://godbolt.org/z/nYTGcKxTv

Polecam ustawić w kompilatorze flagi (dla gcc/clang): -Wall -Wextra -Werror -pedantic i w ramach testowania -fsanitize=address,undefined. o automatycznie wyłapie ci więcej błędów.
Dla msvc to /W4 /WX i w ramach testowanie /fsanitize=address /Zi

1 użytkowników online, w tym zalogowanych: 0, gości: 1