Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: this = NULL

Touho [24.11.2012 01:36:59]

#

Yritin sijoittaa this = NULL. Kääntäjä ei anna tehdä sitä suoraan, mutta alla olevalla koodilla se on mahdollista. Ilmeni kuitenkin, että vaikka voisikin sijoittaa this = NULL, this ei saa uutta arvoa. Se ei siis ole pointteri vaan tapa päästä käsiksi objektin pointteriin.

#include <iostream>

using namespace std;

class Object {
public:
	void setNull(const Object * const &obj) {
		Object * & obj2 = const_cast<Object * &>(obj);
		obj2 = NULL;
		cout << "obj2 = " << obj2 << endl;
	}

	Object() {
		const Object * const thisCopy = this;

		cout << "thisCopy = " << thisCopy << endl;
		setNull(thisCopy);
		cout << "thisCopy = " << thisCopy << endl;

		cout << "this = " << this << endl;
		setNull(this);
		cout << "this = " << this << endl;
	}
};

int main (void) {
	Object();
	return 0;
}

Koodi printtaa:

thisCopy = 0x7fff5fbff99f
obj2 = 0
thisCopy = 0
this = 0x7fff5fbff99f
obj2 = 0
this = 0x7fff5fbff99f

Tzaeru [24.11.2012 05:58:12]

#

Jos en nyt väärin muista, C++:n standardin mukaan this-osoitinta (se on kyllä osoitin, joka lähetetään ei-staattisille metodeille piilotettuna argumenttina) ei tule saada muokata, jolloin standardin toteuttava kääntäjä nyt ei vain näin salli tehtävän.

Vaikka saisitkin kääntäjän lopettamaan virheilmoituksen annon, luultavasti se ei kuitenkaan toteuta sitä koodia joka muokkaa this-osoittimen muistiosoitteessa olevaa arvoa; Itse virheilmoitus saattaa tulla vain jos kääntäjäpalikka huomaa suoran arvon asetuksen. Tahi sitten kääntäjä toteuttaa sen siten, että 'this' korvataan jollain piilossa pidetyllä osoitteella, jota ei sallita muoksittavan.

Metabolix [24.11.2012 13:28:47]

#

Olet varmaan jotenkin ymmärtänyt oman koodisi väärin. Luot uuden osoittimen thisCopy, välität parametrina viittauksen tähän osoittimeen ja muutat tämän osoittimen arvoa. Koodisi on toiminnaltaan ihan vastaava kuin tämä:

int main() {
	int olio;
	int* p_this = &olio;
	int* p_thisCopy = p_this;
	p_thisCopy = 0;
	// p_thisCopy == 0 mutta p_this == &olio.
}

Millään kikkailulla (*, ks. alla) tässä p_thisCopy-muuttujasta ei saa selville p_this-muuttujan sijaintia. Idealtaan vastaavan demonstraation voisi kirjoittaa myös näin:

int main() {
	int a = 1;
	a = 0;
	// a == 0 mutta silti tietenkin 1 == 1
}

Edit: Luin huonosti, edeltävä osa viestistä on turha. Loppuosa pätee kuitenkin.

Jotta voisit muuttaa this-osoittimen arvoa, tarvitsisit osoittimen tai muuttujan, joka osoittaisi this-osoittimeen. Kuitenkin standardin mukaan this-osoitin on prvalue eli pure rvalue eli pelkkä arvo ilman määrättyä sijaintia muistissa – samoin kuin vaikka luku 1 tai lauseke 1+1. Niinpä et voi millään luotettavalla tavalla muuttaa arvoa.

*) Tietenkin kaikki on mahdollista, jos yhteensopivuus ei ole kynnyskysymys. Voisit siis tehdä kääntäjästä ja arkkitehtuurista (tarkemmin sanoen ABI:sta) riippuvaisen viritelmän. Tällainen viritelmä voi kuitenkin hajota pienestäkin muutoksesta kääntäjässä tai olla jopa riippuvainen muusta käännettävästä koodista, joten mikään huippuratkaisu ei ole kyseessä.

Esimerkiksi tämä koodi toimii x86-arkkitehtuurilla ainakin GCC:n oletusasetuksilla, mutta x86_64-arkkitehtuurilla koodi taas ei toimi, koska this-osoitin ei ole muistissa vaan rekistereissä.

#include <cstdio>
#include <cassert>

struct Object {
	Object() {
		assert(sizeof(Object*) == 4);
		Object** ptr;
		ptr = (Object**)&ptr;
		while (*ptr != this) ++ptr;
		std::printf("%p == %p\n", this, *ptr); // 0xffb9bacf == 0xffb9bacf
		*ptr = 0;
		std::printf("%p == %p\n", this, *ptr); // (nil) == (nil)
	}
};

Touho [24.11.2012 13:52:49]

#

Metabolix, thisCopy vain demonstroi, miten setNull() toimii. Sen jälkeen homma tehdään itse thissillä.

EDIT: koodi siis näyttää, että thisCopy(kopio thissistä) ja this toimivat eri tavoin.

Metabolix [24.11.2012 22:30:47]

#

Oho, luin huonosti. Sinänsä selostamani asia ei kuitenkaan muutu miksikään: this on prvalue eli pelkkä arvo eikä objekti, ja kun annat funktion parametrina const-viittauksen siihen, siitä luodaan väliaikainen objekti. Annan uuden esimerkin:

#include <iostream>
void muuta(int const& i) {
	int& j(const_cast<int&>(i));
	j = 0;
}
int main() {
	muuta(1);
	std::cout << 1 << std::endl;
}

Tässä siis 1 on vastaava asia kuin this: se on vain arvo ja sitä ei voi muuttaa. Koodi kuitenkin kääntyy, koska viittausta varten tehdään välikainen int-objekti, johon sijoitetaan arvo 1. Funktio ei tietenkään pysty muuttamaan lukua 1, vaan funktio muuttaa vain väliaikaista int-objektia, jossa ensin on arvo 1. (En jaksa tarkistaa, mutta standardi ei varmaankaan takaa koodin toimivuutta vaan ohjelma voi jollain järjestelmällä kaatua.)

Asiaa voi ajatella myös niin, että jos this sijaitsisi tietyssä paikassa muistissa ja olisi muokattavissa, sillä pitäisi olla osoite eli lausekkeen &this pitäisi toimia.

Touho [25.11.2012 17:33:36]

#

Aivan! Nyt ymmärsin yskän. :)

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta