Archief - protected: verschil Java/C++

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

forloRn_

Legacy Member
Ik heb hieronder dezelfde code staan in Java en C++. De Java-versie werkt zonder problemen maar de C++-versie compileert niet. 't Kan aan mijn kater liggen maar ik zie echt niet waarom. Niet meteen weglopen want het is eenvoudige code hoor.

Java:
Code:
import java.util.*;

abstract class Entity {
	public abstract void collidesWith(Entity other);
	
	protected void collidedWith(Spaceship spaceship) {}
	protected void collidedWith(Asteroid asteroid) {}
}

class Spaceship extends Entity {
	public void collidesWith(Entity other) {
		other.collidedWith(this);
	}
	
	protected void collidedWith(Asteroid asteroid) {
		System.out.println("spaceship destroyed");
	}
}

class Asteroid extends Entity {
	public void collidesWith(Entity other) {
		other.collidedWith(this);
	}
	
	protected void collidedWith(Spaceship spaceship) {
		System.out.println("asteroid doesn't have a scratch");
	}
}

public class DoubleDispatch {

	public static void main(String[] args) {
		Entity spaceship = new Spaceship();
		Entity asteroid = new Asteroid();
		
		List<Entity> entities = new ArrayList<Entity>();
		entities.add(spaceship);
		entities.add(asteroid);

		for (int i = 0; i < entities.size(); i++) {
			Entity current = entities.get(i);
			
			for (int j = i + 1; j < entities.size(); j++) {
				Entity next = entities.get(j);
				
				if (overlap(current, next)) {
					current.collidesWith(next);
					next.collidesWith(current);
				}
			}
		}		
	}
	
	private static boolean overlap(Entity e1, Entity e2) {
		return true;
	}
}

C++:
Code:
/*
 * doubledispatch.cpp
 *
 *  Created on: Sep 25, 2009
 *      Author: forlorn
 */

#include <iostream>
#include <vector>

using namespace std;

class Entity;
class Spaceship;
class Asteroid;
bool overlap(const Entity & e1, const Entity & e2);

class Entity {
public:
	virtual void collidesWith(Entity & other) const = 0;

protected:
	virtual void collidedWith(const Spaceship & spaceship) {}
	virtual void collidedWith(const Asteroid & asteroid) {}
};

class Spaceship: public Entity {
public:
	void collidesWith(Entity & other) const {
		other.collidedWith(*this);
	}

protected:
	void collidedWith(const Asteroid & asteroid) {
		cout << "spaceship destroyed" << endl;
	}
};

class Asteroid: public Entity {
public:
	void collidesWith(Entity & other) const {
		other.collidedWith(*this);
	}

protected:
	void collidedWith(const Spaceship & spaceship) {
		cout << "asteroid doesn't have a scratch" << endl;
	}
};

int main() {
	Spaceship spaceship;
	Asteroid asteroid;

	vector<Entity *> entities;
	entities.push_back(&spaceship);
	entities.push_back(&asteroid);

	for (vector<Entity *>::iterator current = entities.begin(); current != entities.end(); current++) {
		for (vector<Entity *>::iterator next = current + 1; next != entities.end(); next++) {
			if (overlap(**current, **next)) {
				(*current)->collidesWith(**next);
				(*next)->collidesWith(**current);
			}
		}
	}

	return 0;
}

bool overlap(const Entity & e1, const Entity & e2) {
	return true;
}

Output van g++:
Code:
../doubledispatch.cpp: In member function ‘virtual void Spaceship::collidesWith(Entity&) const’:
../doubledispatch.cpp:23: error: ‘virtual void Entity::collidedWith(const Spaceship&)’ is protected
../doubledispatch.cpp:30: error: within this context
../doubledispatch.cpp: In member function ‘virtual void Asteroid::collidesWith(Entity&) const’:
../doubledispatch.cpp:24: error: ‘virtual void Entity::collidedWith(const Asteroid&)’ is protected
../doubledispatch.cpp:42: error: within this context

Als je protected vervangt door public werkt het wel. Iemand een idee?

Parnakra

Legacy Member
Als ik mij niet vergis, laat C++ niet toe dat je protected virtual methods uit je base class aanroept via een instantiatie van je derived class, maar moet je ofwel gaan expliciet gaan casten, ofwel via een intermediate base-object pointer je derived object aanroepen.

Maar als ik je code bekijk, roep je de methodes wel degelijk op via een (pointer naar) een Entity, en dus base class. :s

Op deze pc heb ik momenteel geen C++ compiler staan, maar je kan het misschien eens proberen met een static_cast?

Kemblin

Legacy Member
volgens mij kan het met public inheritance enkel zo

Code:
class Spaceship: public Entity {
protected:
	void collidesWith(Entity & other) const {
		other.collidedWith(*this);
	}

	void collidedWith(const Asteroid & asteroid) {
		cout << "spaceship destroyed" << endl;
	}
};

class Asteroid: public Entity {
protected:
	void collidesWith(Entity & other) const {
		other.collidedWith(*this);
	}

	void collidedWith(const Spaceship & spaceship) {
		cout << "asteroid doesn't have a scratch" << endl;
	}
};


hier onderaan staan wat voorbeeldjes
http://www.parashift.com/c++-faq-lite/private-inheritance.html#bottom

forloRn_

Legacy Member
Ja, casten heeft geen zin aangezien het argument al een (referentie naar een) Entity is. Ik heb het desalniettemin eens geprobeerd maar het resultaat blijft hetzelfde.

collidesWith() protected maken is geen optie want ik moet die method kunnen aanroepen vanuit een andere klasse.

Het vreemde is dat wanneer je voor de lol other vervangt door this, het ding wél compileert (nadat je hier en daar wat const bij zet). :wtf: Visibility werkt toch op klasseniveau, en niet op objectniveau?

Kemblin

Legacy Member
tis toch vrij logisch dat dat niet werkt?

protected items van klasse A kan je enkel accessen in klasses B als die afgeleid is van klasse A, en je kan de protected methods ENKEL uitvoeren op je eigen (this) object.

Wat jij hier probeert met other.collidedWith(*this); is een protected methode aanroepen alsof ze public is.

forloRn_

Legacy Member
In Java werkt het wel dus zo logisch zal het niet zijn.

Nee, wat ik probeer is een protected method aanroepen in mijn base class, die overridden wordt in mijn derived classes. Toch een mooi voorbeeld van polymorfisme, of niet?

En wat die this betreft:
Code:
/*
 * Visibility.cpp
 *
 *  Created on: Sep 26, 2009
 *      Author: forlorn
 */

#include <iostream>

using namespace std;

class SomeClass {
public:
	SomeClass(int value): value(value) {}

	void swap(SomeClass & rhs) {
		int t = value;
		value = rhs.value;
		rhs.value = t;
	}

	int getValue() const {
		return value;
	}

private:
	int value;
};

int main() {
	SomeClass a(5), b(6);

	a.swap(b);

	cout << "a: " << a.getValue() << "\nb: " << b.getValue() << endl;
}

Zie a eens aan b zijn privates komen.

Kemblin

Legacy Member
hmm ok, ik zwijg :p
heb blijkbaar altijd al gedacht dat visibility op object niveau werkt...

forloRn_

Legacy Member
't Is misschien geen typisch voorbeeld, maar van zodra je een referentie naar een base class gebruikt om methodes van je derived classes aan te roepen, gebruik je polymorfisme, daarover is geen discussie mogelijk.

Waar ik je wel in gelijk geef, is het feit dat dit geen propere code is. Je moet inderdaad voor elke nieuwe entity je base class zitten wijzigen.

Toevallig heb ik ontdekt dat dit voorbeeld bijna exact in More Effective C++ beschreven staat. De auteur werkt dit voorbeeld uit, somt de nadelen ervan op, geeft een andere mogelijke oplossing met even veel nadelen, en besluit dan dat hij het ook niet weet. :woohoo:

Mijn vraag: hoe wordt collision detection waarbij het resultaat afhangt van beide types entities dan in de praktijk gedaan? Er komen zoveel games uit die allemaal collision detection hebben, er moet ondertussen toch één of andere hapklare oplossing voor zijn?

Los daarvan: voorlopig gebruik ik deze oplossing, waarbij ik nog steeds mijn methode als protected declareer, maar wel elke derived class een friend maak van de base class. De base class moet sowieso elke derived class kennen dus daar zie ik geen bijkomende nadelen.

apa

Legacy Member
Best vreemde code dit.

Protected members zijn toegankelijk voor de derived classes. In die zin is het logisch dat de klassen Spaceship en Asteroid de methoden this.collidedWith(Spaceship spaceship) en this.collidedWith(Asteroid asteroid) kunnen aanroepen.

In de volgende methode doe je echter wat anders:
Code:
public:
	void collidesWith(Entity & other) const {
		other.collidedWith(*this);
	}
Hier cast je other naar de Entity class en die heeft geen publieke collidedWith methode. Logisch dus dat je een compiler error krijgt.

In Java kan dat wel omdat de "protected" modifier ervoor zorgt dat de methode ook zichtbaar is voor objecten van klassen uit dezelfde package. In dat opzicht is protected als het ware een combinatie van de "protected" en de "internal" modifiers uit C# (of "protected" en "friend" uit C++).

forloRn_

Legacy Member
Sterk. :eek:

Als je ze in verschillende packages stopt, compileert het in Java inderdaad ook niet: "The method collidedWith(Asteroid) from the type Entity is not visible."

De quick fix van Eclipse is "Change visibility of 'collidedWith' to 'protected'." Die is dus ook in de war. :lol:

En aan een ander object zijn private members zitten, lukt waarschijnlijk omdat dat ander object van exact dezelfde klasse is. :oink:

apa

Legacy Member
Ik ben geen expert in C++ of Java, maar in C# kan je in principe niet aan de private members van een klasse tenzij je Reflection gebruikt (dat komt ongeveer neer op het rechtstreeks manipuleren van pointers: iets wat je in C++ ook zou kunnen).

forloRn_

Legacy Member
Nocturn zei:
Is collision detection geen geometrische eigenschap? Waarom zou het moeten afhangen van het type van het object. Dat was nog iets dat me opviel aan je code. Je gebruikte:
virtual void collidedWith(const Spaceship & spaceship) {}
ipv
virtual bool collidedWith(const Spaceship & spaceship) const {}

Uw collision detection moet zich enkel maar bezighouden of er iets gebotst is met iets anders. Ik zie dus niet in waarom die functie niet const moet zijn. Wat er moet gebeuren als er een collision is, is niet de taak van de collision detection. Anders zou het niet collision detection noemen maar collision_detection_with_space_ship_explosion_and_asteroid_debris_generation_unless_the_space_ship_survives_then_it_is_.... ;P

Een van de fouten dat ik gemaakt heb met mijn eerste 3d game was dat ik geen onderscheid had gemaakt tussen mijn scenegraph en ruimtelijke tree. Nuja, alles werkt en code al bij al viel nog mee, maar achteraf gezien had ik beter het geometrische van het functionele moeten scheiden.

Voor meer over collision detection, rendering, etc...
Real-Time Rendering Home Page
Dat boek is goud waard en is zeker geen verloren geld om het aan te schaffen.

Neenee, ik heb het niet over collision detection op zich (nagaan of objecten fysiek overlappen) maar over de gevolgen van collision detection. Een spaceship dat botst met een asteroid zal anders reageren dan wanneer het botst met een powerup.

forloRn_

Legacy Member
Ik heb nooit beweerd dat asteroids geen powerups kunnen krijgen. :)

't Project staat nog in zijn kinderschoenen maar het vordert wel. Zoals je al kunt raden is het een Asteroids-kloon maar dan volledig gebaseerd op MVC en met mooie SDL graphics. Bedoeling was louter C++ wat op te frissen.

Ik gebruik interpolatie (met dank aan dit artikel van iemand die ook op dit forum zit) zodat alles er ongelooflijk smooooth uitziet. De framerate ligt momenteel rond de 650 fps dus ik heb nog wel wat marge om er meer fancy dingen in te stoppen. :)
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan