Przeładowany operator new[] i delete [] (Misja w nadprzestrzeń c++14/17), różny rozmiar w new i delete

1

Witam,
mam pytanko, w książce J. Grębosza o C++14/17 (Misja w nadprzestrzeń) w rozdziale 3 podrozdziale 3 jest taki kawałek programu:

 // Misja w nadprzestrzeń C++14/17, program z paragrafu 3.3.2


#include <iostream>
using namespace std;
/////////////////////////////////////////////////////////////////////////////////////////
class alignas(32) Twektorek												// `1
{
public :
	double x, y, z;
	//--- konstruktor -----
	Twektorek (double a=0, double b=0, double c=0) : x (a), y (b), z(c)
	{
	}
	//---------------------------------------------
	void wypisz_zawartosc() {
		cout << "\t zawartosc x= "<< x << ", y= "<< y <<", z= "<< z << endl;
	}

	// przeładowanie operatorów new i delete na użytek tej klasy
	void * operator new (size_t rozmiar, align_val_t 	w);
	void * operator new[ ] (size_t rozmiar, align_val_t w);

	void operator delete (void * wsk, size_t rozmiar, align_val_t wyrownanie);
	void operator delete[ ] (void * wsk, size_t rozmiar, align_val_t wyrownanie);
};
/////////////////////////////////////////////////////////////////////////////////////////
void * Twektorek::operator new ( size_t rozmiar, align_val_t  wyrownanie)  		// `2
{
	cout << "\t(nA) new(rozmiar = " 	<< rozmiar <<", wyrownanie = "
		 << static_cast<size_t>( wyrownanie) << ")" << endl;

	return ( aligned_alloc(static_cast<size_t>( wyrownanie), rozmiar));
}
//*******************************************************************
void * Twektorek::operator new[ ] (size_t rozmiar, align_val_t wyrownanie)		// `3
{
	cout << "\t(nB) new[ ] (rozmiar = " << rozmiar
		 << ", wyrownanie " << static_cast<size_t>( wyrownanie) <<endl;
	return (aligned_alloc(static_cast<size_t>( wyrownanie), rozmiar) );
}
//*******************************************************************
void  Twektorek::operator delete (void * wsk, size_t rozmiar, align_val_t wyrownanie)	// `4
{
	if ( wsk ) {
		cout << "\t(dA) delete (void *, rozmiar = " << rozmiar
			 << ", wyrownanie = " << static_cast<size_t>( wyrownanie)<< ")" << endl;
		// nasze własne akcje, a potem...
		free(wsk);
	}
}
//*******************************************************************
void Twektorek::operator delete[ ] (void * wsk, size_t rozmiar, align_val_t wyrownanie) 	// `5
{
	if(wsk) {
		cout << "\t(dB) delete[](void * wsk, rozmiar = "<< rozmiar
			 << ", wyrownanie =" << static_cast<size_t>( wyrownanie) << ")" << endl ;
		free( wsk );
	}
}
//*******************************************************************
int main()
{
	cout << "Wyrownanie klasy Twektorek zostalo ustawione na " << alignof(Twektorek) <<endl;

	cout << "Wytworzenie pojedynczego obiektu \n";
	auto *w1 = new Twektorek ( 1, 1, 1 ); 									// `6
	w1->wypisz_zawartosc();
	delete w1;														// `7

	cout << "Rezerwacja tablicy trzech wektorkow" << endl;

	Twektorek * wtab = new Twektorek[3];									// `8
	for ( int i = 0; i < 3 ; ++i ) {
		wtab[i].wypisz_zawartosc();
	}
	delete [ ] wtab;													// `9
}

Po uruchomieniu tego programu np w https://wandbox.org/ jego wynikiem jest:

Start
Wyrownanie klasy Twektorek zostalo ustawione na 32
Wytworzenie pojedynczego obiektu 
	(nA) new(rozmiar = 32, wyrownanie = 32)
	 zawartosc x= 1, y= 1, z= 1
	(dA) delete (void *, rozmiar = 32, wyrownanie = 32)
Rezerwacja tablicy trzech wektorkow
	(nB) new[ ] (rozmiar = 96, wyrownanie 32
	 zawartosc x= 0, y= 0, z= 0
	 zawartosc x= 0, y= 0, z= 0
	 zawartosc x= 0, y= 0, z= 0
	(dB) delete[](void * wsk, rozmiar = 3616, wyrownanie =32)
0
Finish

W książce jest tak samo. Dlaczego operator new[] otrzymuje rozmiar 96 a rozmiar delete[] kasujący tą tablicę otrzymuje rozmiar 3616?
Z góry dziękuję za pomoc :)

4

To wygląda na bug w gcc.
Z clang jest ok.
Address sanitizer szaleje na gcc: https://godbolt.org/z/5d6sWz

3

Ciekawe, że jak doda się destruktor wszystko jest ok.

1

Ale co ciekawe ten destruktor musi być ~Foo() {} bo z ~Foo() = default; ten błąd już występuje
EDIT: ale już Foo::~Foo() = default; (non-inline) działa ok. No fajnie znalezisko ;)

3
alagner napisał(a):

Ale co ciekawe ten destruktor musi być ~Foo() {} bo z ~Foo() = default; ten błąd już występuje

EDIT: ale już Foo::~Foo() = default; (non-inline) działa ok. No fajnie znalezisko ;)

to chyba logiczne, bo ~Foo() = default; działa dokładnie tak samo jak brak deklaracji. ~Foo() {} to definicja własnego destruktora, który zachowuje się jak domyślny.

Wersja Foo::~Foo() = default; oznacza, ze najpierw zadeklarowałeś destruktor jako z własna definicją, a w definicji wymuszasz default.

To jest ostateczny dowód na to, że to jest śmieszny bug gcc.

0

Raport a gcc został potwierdzony, więc pewnie będzie to naprawione.

0

Przecież w delete nigdy nie podajemy rozmiaru.

p = new int[1000];

delete p; // nigdy żadne: p[1000]!

Takie coś jedynie w paskalu funkcjonowało i ze 100 lat temu chyba:
FreeMem(ptr, size)

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