konstruktor w C++

0

Cześć :)
Czy konstruktor w C++ jest statyczny

4

To jest czasem dość sporna kwestia, związana z definicją, ale ja bym powiedział że nie jest, bo pracuje na rzecz obiektu (nawet jeśli dopiero jest on w trakcie tworzenia) i ma dostęp do this.

0

a w JAVIE jest?

2

W JAvie ma dostęp do this i może wołać metody niestatyczne, więc statyczny nie jest.
Ale nie można wywołać konstruktora na rzecz istniejącego obiektu.
Dlatego konstruktor to po prostu... konstruktor.

0

Zacznijmy najpierw od tego, że konstruktor musi być statyczny, bo jakby go wywołała jakaś inna funkcja? A this jest po prostu w ecx albo na stosie.

0

Cały myk w tym, że najpierw jest alokowana pamięć na obiekt i potem wskaźnik na tę pamięć jest przekazywany jako this do konstruktora, więc konstruktor jest metodą na niezainicjowanym obiekcie.

Statyczny konstruktor to zapewne konstruktor inicjalizujący statyczne pola. W C++ chyba czegoś takiego nie ma, bo i nie ma czegoś takiego jak ładowanie klas. Jest statyczne łączenie plików object, a więc kolejność odpalania statycznych konstruktorów zależałaby od kolejności plików object podanych do linkera. Mało rozsądne rozwiązanie.

W Javie jest coś takiego jak konstruktor statyczny, tzn wypełniający pola typu static. Jest on blokiem kodu opatrzonym słówkiem 'static'.
http://stackoverflow.com/questions/335311/static-initializer-in-java

0

W Javie konstruktor JEST statyczny

0

Taki kod:

public class Main {

    public static void main(String[] args) throws Exception {
        Integer i = new Integer(5);
        System.out.println(i.toString());
    }
}

Po przepuszczeniu przez 'javap -c' daje:

Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #2                  // class java/lang/Integer
       3: dup           
       4: iconst_5      
       5: invokespecial #3                  // Method java/lang/Integer."<init>":(I)V
       8: astore_1      
       9: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: aload_1       
      13: invokevirtual #5                  // Method java/lang/Integer.toString:()Ljava/lang/String;
      16: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      19: return        
}

Instrukcja 'new' tworzy niezainicjowany obiekt na stercie. Instrukcja 'dup' duplikuje najwyższy element na stosie, czyli referencję do tego świeżego niezainicjowanego obiektu. Następnie jest instrukcja 'iconst_5' odkładająca na stos liczbę 5 oraz instrukcja 'invokespecial' która odpala konstruktur, pobierający jako parametry referencję do niezainicjowanego obiektu oraz jeden parametr. Na stosie zostaje referencja do poprzednio utworzonego obiektu, ale teraz jest już zainicjowany przez konstruktor. 'astore_1' zdejmuje tą referencję i wkłada ją w slot numer 1 na zmienne lokalne (ZTCP to maszyna Javy jest w części stosowa a w części rejestrowa).

0

tak pisze w Thinkingu in Java. Poza tym, dlaczego uważasz, ze Twój kod oznaczać miałby, że konstruktor jest statyczny/

0

W którym miejscu?

Może pomieszałeś zdanie. Np "jest konstruktor statyczny" zamieniłeś na "konstruktor jest statyczny"? :P

Poza tym, dlaczego uważasz, ze Twój kod oznaczać miałby, że konstruktor jest statyczny/

Bajtkod pokazuje że konstruktor obiektu jest niestatyczny, bo wymaga referencji do niezainicjowanego obiektu. Zresztą jak do tej pory nikt nie sprecyzował co to znaczy 'konstruktor statyczny', więc ciężko się kłócić o takie coś ;]

0

Okazuje się również, że konstruktor jest również metodą statyczną klasy...

Rozdział: Informacje o typach
Podrodział: Obiket class.

Bajtkod pokazuje że konstruktor obiektu jest niestatyczny, bo wymaga referencji do niezainicjowanego obiektu.

A dlaczego tak twierdzisz? To jeszcze o niczym nie świadczy. :)

0

@n0name_l:
W Javie też jest coś co można by nazwać konstruktorem statycznym: http://ideone.com/NO2CTi

A dlaczego tak twierdzisz? To jeszcze o niczym nie świadczy.

Jak wynika z bajtkodu, konstruktor wymaga jako parametru referencji do niezainicjowanego obiektu. Tak to się dzieje na najniższym poziomie (pomijając JITowanie ofc).

W C++ie konstruktor też jest mocno podobny, tzn w środku masz dostęp do thisa, więc jakiś obiekt musi być. Różnica między metodami statycznymi, a niestatycznymi to właśnie obecność thisa - konstruktor go ma, więc jest niestatyczny :]

Alokowanie niezainicjowanego obiektu jest czymś oddzielnym niż inicjowanie/ konstruowanie już zaalokowanego obiektu. Alokować można na wiele sposobów. Konstrukcja polega po prostu na przesłaniu referencji obiektu do konstruktora.

W C++ akurat jest możliwe wywołanie konstruktora na rzecz wskaźnika, czyli dowolnego kawałka pamięci. Odbywa się to za pomocą placement new: http://en.wikipedia.org/wiki/Placement_syntax
W skrócie:
new Typ(parametry) - tutaj new tworzy niezainicjowany obiekt i podaje go do konstruktora
new (wskaźnik) Typ(parametry) - tutaj new nic nie robi, jest tylko przekazanie wskaźnika (i parametrów oczywiście) do konstruktora

1

Zacytuję stronę wiki do której linka dał @Wibowit:

The basic problem is that the constructor is a peculiar function; when it starts off, there is no object, only raw memory. And by the time it finishes, you have a fully initialized object. Therefore i) The constructor cannot be called on an object ii) However, it needs to access (and initialize) non-static members.

Nie ma sensu szufladkowanie czy konstruktor jest statyczny czy nie. Trochę jest, a trochę nie jest.
Konstruktor to konstruktor.

0

Cały czas chodzi o to, że nie można stwierdzić, że konstruktor nie jest statyczny, bo bierze referencję do obiektu. Chodzi o to, że nie można tak powiedzieć, bo w tym miejscu pamięci jeszcze nie istnieje obiekt. Dopiero konstruktor biorąc referencję do tego obszaru zainicjalizuje go tworząc obiekt. Z tego punktu widzenia konstruktor nie jest metodą niestatyczną.
Dlatego ja przyjmuję za Thining in JAVA, że konstruktor jest statyczny.

0

Tyle, że ten niezainicjowany obiekt może wyciec na samym początku konstruktora i wtedy cała gama innych funkcji może operować na niezainicjowanym obiekcie. A skoro wg ciebie niezainicjowany obiekt to nie obiekt, to co z obiektami wyciekniętymi z konstruktora? :]

Azarien:
Trochę wyjąłeś z kontekstu. The constructor cannot be called on an object oznacza, że nie da się napisać obiekt.Konstruktor(). Da się natomiast odpalić konstruktor na czymkolwiek za pomocą placement new, czyli nieco innej składni do zrobienia dokładnie tego samego. Placement new można odpalić na już zainicjowanym obiekcie i to dowolnej klasy.

0

Tyle, że ten niezainicjowany obiekt może wyciec na samym początku konstruktora i wtedy cała gama innych funkcji może operować na niezainicjowanym obiekcie. A skoro wg ciebie niezainicjowany obiekt to nie obiekt, to co z obiektami wyciekniętymi z konstruktora? :]

W jakim sensie może wyciec? Jeżeli konstruktor byłby niestatyczne to nie może wyciec? Jeżeli też może to jaka jest odpowiedź na Twoje pytanie?

0

Wyciec czyli przekazać thisa do metody z innego obiektu lub wpakować do zewnętrznej kolekcji - pośrednio (przez metody własnej klasy) lub bezpośrednio (czyli prosto z konstruktora).

Chodzi np o to, że w konstruktorze mogę sobie ustawiać pole x na 5, ale przed ustawieniem np wrzucam thisa do globalnej tablicy i jakiś inny wątek dobiera się do tej referencji i wypisuje x'a. Wtedy może wypisać 0, jeżeli konstruktor nie dojdzie jeszcze do przypisania.

Jedyny nieoczywisty szczegół to moment w którym ustawiany jest wskaźnik do Vtable i inne pierdółki wspólne dla wszystkich obiektów. Czy to jest ustawiane przez konstruktor czy może przed konstruktorem, w miejscu tuż sprzed wywołania konstruktora?
I czy obecność poprawnego wskaźnika do Vtable już robi z kawałka pamięci coś co można nazwać obiektem? W C++ obiekty bez metod wirtualnych nie potrzebują i nie posiadają wskaźnika do Vtable, więc nawet ciężko odróżnić wtedy obiekt od nie-obiektu.

Oprócz tego sprawa jest oczywista: konstruktor bierze jako parametr niezainicjowany obiekt, a po zakończeniu wywołania zostawia zainicjowany obiekt.
Sama inicjalizacja równie dobrze może też być leniwa, np w obiekcie jest flaga initialized ustawiona początkowo na false (z tym, że na false w Javie ustawiać nie trzeba bo to wartość domyślna początkowa) i w każdej metodzie jest kawałek kodu typu:

if (!initialized) {
  init();
  initialized = true;
}
0

Smieszne pytanie. Konstruktor to jest tylko cukier na metode inicjalizacujaca obiekt, nic mniej i nic wiecej.
Kiedys bylo tak:

struct Foo {
  
};

void init(Foo* f) { ... }

Potem tak:

struct Foo {
  Foo() { ... }
}

Jedyne co sie zmienilo, to to ze mozna wrzucac metody do obiektu i konstruktora nie trzeba jawnie wywolywac, ale nadal nie jest on w zadnym wypadku statyczny i nie jest on w zadnym wypadku wymagany do tego, zeby obiekt miec.

#include <iostream>
#include <cstdlib>
using namespace std;

class Foo {
private:
	int a;
	Foo() {}
public:
    void setA(int v) { a = v; }
	void printA() { cout << a << endl; }
};

int main() {
	Foo* obj = (Foo*)malloc(sizeof(Foo));
	obj->setA(10);
	obj->printA();
	free(obj);
	return 0;
}

http://ideone.com/trE5g7

Foo::setA(int):
	push	rbp
	mov	rbp, rsp
	mov	QWORD PTR [rbp-8], rdi
	mov	DWORD PTR [rbp-12], esi
	mov	rax, QWORD PTR [rbp-8]
	mov	edx, DWORD PTR [rbp-12]
	mov	DWORD PTR [rax], edx
	pop	rbp
	ret
Foo::printA():
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
	mov	QWORD PTR [rbp-8], rdi
	mov	rax, QWORD PTR [rbp-8]
	mov	eax, DWORD PTR [rax]
	mov	esi, eax
	mov	edi, OFFSET FLAT:std::cout
	call	std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
	mov	esi, OFFSET FLAT:std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
	mov	rdi, rax
	call	std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
	leave
	ret
main:
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
	mov	edi, 4
	call	malloc
	mov	QWORD PTR [rbp-8], rax
	mov	rax, QWORD PTR [rbp-8]
	mov	esi, 10
	mov	rdi, rax
	call	Foo::setA(int)
	mov	rax, QWORD PTR [rbp-8]
	mov	rdi, rax
	call	Foo::printA()
	mov	rax, QWORD PTR [rbp-8]
	mov	rdi, rax
	call	free
	mov	eax, 0
	leave
	ret
__static_initialization_and_destruction_0(int, int):
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
	mov	DWORD PTR [rbp-4], edi
	mov	DWORD PTR [rbp-8], esi
	cmp	DWORD PTR [rbp-4], 1
	jne	.L5
	cmp	DWORD PTR [rbp-8], 65535
	jne	.L5
	mov	edi, OFFSET FLAT:std::__ioinit
	call	std::ios_base::Init::Init()
	mov	edx, OFFSET FLAT:__dso_handle
	mov	esi, OFFSET FLAT:std::__ioinit
	mov	edi, OFFSET FLAT:std::ios_base::Init::~Init()
	call	__cxa_atexit
.L5:
	leave
	ret
	push	rbp
	mov	rbp, rsp
	mov	esi, 65535
	mov	edi, 1
	call	__static_initialization_and_destruction_0(int, int)
	pop	rbp
	ret

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