Dlaczego nie możemy tworzyć obiektów łatwych do zbudowania za pomocą malloc, jeśli prosty domyślny konstruktor nie wykonuje żadnej akcji?


14

Mam trudności ze zrozumieniem poniższego akapitu cytowanego z cpreferencji na temat trywialnego domyślnego konstruktora. Przeszukałem stackoverflow, ale nadal nie otrzymałem jasnej odpowiedzi. Więc proszę o pomoc.

Trywialny domyślny konstruktor to konstruktor, który nie wykonuje żadnej akcji. Wszystkie typy danych kompatybilne z językiem C (typy POD) są trywialnie domyślne. W przeciwieństwie do C nie można jednak tworzyć obiektów z trywialnymi domyślnymi konstruktorami, po prostu ponownie interpretując odpowiednio wyrównane miejsce do przechowywania, takie jak pamięć przydzielona ze std :: malloc: umieszczenie-nowe jest wymagane do formalnego wprowadzenia nowego obiektu i uniknięcia potencjalnie niezdefiniowanego zachowania.

W szczególności, jeśli trywialny domyślny konstruktor nic nie robi, dlaczego nie możemy ponownie zinterpretować pamięci i udawać, że istnieje obiekt o danym typie? Czy mógłbyś podać kilka przykładów potencjalnie niezdefiniowanego zachowania, które mogłoby to spowodować?


Najważniejszym zadaniem kompilatora nie jest kompilacja kodu źródłowego, ale odrzucenie prawdopodobnie niepoprawnego kodu. Nie można tego zrobić, gdy używasz malloc ().
Hans Passant

6
Powód jest bardzo prosty. Im mniej jest szans dla programisty na robienie szalonych rzeczy, tym więcej jest możliwości kompilatora na robienie szalonych rzeczy (agresywne optymalizacje).
n. „zaimki” m.

1
Z podobnych powodów, których po prostu nie możesz *reinterpret_cast<float*>(&someNonFloatObject) = 0.1f;. C ++ ma pojęcie obiektów i czasów życia obiektów, określone na maszynie abstrakcyjnej, i tylko dlatego, że nie ma instrukcji procesora do utworzenia obiektu z pamięci, nie oznacza, że ​​nie ma różnicy na maszynie abstrakcyjnej.
Max Langhof

1
@HansPassant Kompilator, który odrzuca cały kod, odrzuca cały niepoprawny kod. W każdym razie nie jest zadaniem kopilera odrzucanie programów, które mają UB.
n. „zaimki” m.

Odpowiedzi:


7

P0593R5 podaje ten przykład:

struct X { int a, b; };
X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

i wyjaśnia:

Po skompilowaniu za pomocą kompilatora C ++ kod ten ma niezdefiniowane zachowanie, ponieważ p-> próbuje zapisać do podobiektu int obiektu X, a ten program nigdy nie utworzył ani obiektu X, ani podobiektu int.

Na [intro.object] p1,

Obiekt jest tworzony przez definicję, przez nowe wyrażenie, podczas niejawnej zmiany aktywnego elementu unii lub podczas tworzenia obiektu tymczasowego.

... a ten program nie zrobił żadnej z tych rzeczy.

W praktyce to działa, a sytuacja UB jest uważana bardziej za wadę standardu niż cokolwiek innego. Głównym celem tego artykułu jest zaproponowanie sposobu rozwiązania tego problemu i podobnych przypadków bez rozbijania innych rzeczy.


1

Z powodu „czystości”.

Alternatywą i faktycznym status quo było to, że każdy region przechowywania zawierałby jednocześnie wszystkie obiekty mieszczące się w tym magazynie. Niektórzy członkowie komitetu są zaniepokojeni obecnym stanem rzeczy i wiele osób obawiało się, że istnieje nieskończenie wiele obiektów w tym samym miejscu (w wirtualnym, niezainicjowanym stanie).

Nikt nigdy nie był w stanie wykazać logicznego problemu z nieskończoną liczbą obiektów w regionie przechowywania.

Ponieważ mieli różne sekcje standardu, które mówią sprzeczne rzeczy, członkowie komitetu po prostu postanowili poważnie potraktować jedną z najgorszych części normy.

Również używanie literałów łańcuchowych jest surowo zabronione, jeśli naprawdę poważnie traktujesz tę część standardu.


używanie literałów łańcuchowych jest surowo zabronione . Podobny problem dotyczy type_infoobiektów CWG . Czy zgłosiłeś literały ciągów?
Język Lawyer
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.