Direct3D Tutorial 8 / has VS is

The Partridge Family were neither partridges nor a family. Discuss.
Firepath
Posts: 77
Joined: March 10th, 2018, 11:53 pm

Re: Direct3D Tutorial 8 / has VS is

Post by Firepath » March 9th, 2019, 7:07 am

Hi all, apologies for not checking in for a few days. Thanks for inputting. Normally when I start posting on threads they die... On any forum. lol

Papa - it was a question of that specific example but also obviously in applying it general in a similar situation. The extension you talk about is what I was finally understanding with your mouse server example. I guess yeh, like you I am currently in that phase where I'm putting pure encapsulation over anything else and need to unlearn the lessons from university that made it the be-all-end-all.

I fully agree with your rant on children being extensions of the parents. You could access the "private" parent members if they were protected though right? But then if the child becomes a parent, those are now private in the child and the grandchild couldn't see them so only valid for one level of inheritance. Or I might have misunderstood / applied assumptive understanding to this.

Thanks for the links chili, I'm checking them out. I wish I'd gotten into software development straight out of university, and somewhere that makes software properly (FORMAL requirements gathering, design, testing, documentation, etc.) instead of using the "agile" methodology (in my experience this has meant hacking code out and testing / getting customers to test until it works). So yeah I'd love to do thing properly and know proper software engineering but I spend my life with the opportunities I've had available.

I think I need more time experiencing these new-to-me things to get comfortable with them. Eventually something clicks and I get it or at least come to terms with being OK with doing it that way lol.

I've actually been looking at some other youtubes on rule of 3, && (copy asign and copy constructor) and other basic C++ stuff again (by other people, sorry chili :\ lol ). It is good to refresh as I almost completely forget all the whys and hows but when I see the vids I'm understanding and remembering along with it, which is a good sign.

I also think some language features are odd. One in particular that I keep forgetting that is also a thing in C# is private members can be accessed by another instance of the same class. That just kinda blows my mind as it just seems to destroy the point of all this encapsulation that is a major benefit of oop. I don't see a case where this is really that beneficial, and especially not in a way that it is used more commonly than not that would warrant it being a thing. I guess this is one of those things that shows that encapsulation isn't truly what oop is ALL about.

It's amazing that you're (Papa) actually understanding more about what I'm really saying (my hang-ups with language quirks and oop not being as I thought it would be) through what I've actually said, but you're right!

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Direct3D Tutorial 8 / has VS is

Post by albinopapa » March 9th, 2019, 6:58 pm

I respond to these posts in the order that interest me and not in the order the poster has presented them, that warning aside:
One in particular that I keep forgetting that is also a thing in C# is private members can be accessed by another instance of the same class.
Yes, this still surprises me every time I "get away" with it lol. I don't know why it surprises me, but it does, in a good way. I have also been surprised in a bad way ( more disappointing than bad ) because there are situations where I thought "since it works this way, it should work this other way too" but it did not. For instance, iirc, given that class B and C inherit from A, I would have thought that B could access at the very least the protected members and methods inherited from A through a C interface, again if I recall correctly, this is not the case.
I don't see a case where this is really that beneficial, and especially not in a way that it is used more commonly than not that would warrant it being a thing.
I've had varying opinions on this through trying different design strategies. In one case, I took the approach of responsibility of handling what happens after a collision between two objects ( basic interaction between them can be used in this case ). I have a Player that derives from Entity and a Enemy that derives from Entity and each have their own health and damage amounts. I would pass each to each other's HandleCollision() method through a const Entity&.

Code: Select all

player.HandleCollision( enemy );
enemy.HandleCollision( player );
The idea being that each entity would know how to handle being hit, if player had a buff then it could take that into account ( like a shield or magic attack reducer ) as well as knowing it's own position, velocity and size to be able to move itself outside of the area of the other entity. My design allowed for these attributes to be stored in the base Entity class, so I could access the other's data without needing to go through getter functions. Though, for things like GetRect() which usually provides a translated rectangle I'd use that function as opposed to using the position and size data directly. Since the Entity being passed by const &, it prevented me from just updating B's health from A or A's health from B since they wouldn't know what buffs were active.

Another approach separated the collision correction and the damage handling, so a separate Physics procedure or class method would check for collision, adjust the positions and velocities of each, then would still pass B to A::TakeDamage() and A to B::TakeDamage().

Code: Select all

if( physics.Collides( a, b ) )
{
   physics.CorrectOverlap( a, b );
   a.TakeDamage( b.DealDamage() );
   b.TakeDamage( a.DealDamage() );
}
Something I came across a while back is limiting the amount of information you share between objects. This cuts back on dependencies or coupling. The example they gave was, if you were in a checkout line at the store, you'd hand the cashier the amount requested/required, and not your wallet for them to take out the cash. So, I started to take that into consideration in my design and now find it harder to find a reason to pass one instance of an entity to another instance's methods, UNLESS the amount of information needing to be shared is more than a couple of parameters. I hate writing functions that have more than three parameters, four tops, so if that is the case, I'll just pass the damned object and use getters or if I'm feeling lazy access it directly if possible.

So, I'm not going to quote this portion, but let's say you have base class A, child class B and grandchild class C.

Code: Select all

class A
{
public:
	int a;
protected:
	int b;
private:
	int c;
};

class B : public A
{
public:
	int GetA()
	{
		return a;
	}
	int GetB()
	{
		return b;
	}
	int GetC()
	{
		return c;
	}
};

class C : public B
{
public:
	int GetB_A()
	{
		return a;
	}
	int GetB_B()
	{
		return b;
	}
	int GetB_C()
	{
		return c;
	}
};
The only data that is not accessible from the child nor grandchild is the A::c. C even though it's a child of B, it still has access to the protected members of A because it inherits from B which inherits from A. There has been a lot of push against deep hierarchies and this is probably to avoid the "dreaded diamond" where a grandchild might inherit from two parents with the same members or methods causing the compiler to shit the bed. So if you subscribe to this way of thinking ( do away with deep hierarchies ) then you won't have to worry about what the grandchildren classes can or cannot do.
I've actually been looking at some other youtubes on rule of 3...
First, I'd like to say that chili is pretty thick skinned and will probably encourage you to use whatever resource you need to get this stuff.

Getting back to the rule of X.
There's the rule of zero: no destructor, no copy constructor, no move constructor, no copy assignment operator and no move assignment operator.
The old rule of three: copy constructor, copy assignment operator and destructor
The C++11 rule of five: copy constructor, copy assignment operator, move constructor, move assignment operator and destructor.

The only time you need to specify/create the copy or move functions ( and therefore the destructor ) is if your class encapsulates a resource and needs to manage it's lifetime and ownership. For stack based data, there will never be a need to create copy or move functions as the compiler generated ones will work just fine. For heap allocated data you'll need to at the very least create the move functions and declare the copy functions = delete;. If you have a Texture storing a raw pointer to the pixel data, you want to either prevent copying

Code: Select all

Texture(const Texture&) = delete;
Texture& operator=(const Texture&) = delete;
or you must define them as the compiler won't know how to handle copying the raw data from within the heap ( called a deep copy ).
With move semantics, you'll be able to transfer ownership of the raw pointer from one class to another and this is where it actually benefits you. Move semantics doesn't benefit you when your class is nothing but trivially copyable data.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Firepath
Posts: 77
Joined: March 10th, 2018, 11:53 pm

Re: Direct3D Tutorial 8 / has VS is

Post by Firepath » March 9th, 2019, 10:20 pm

Thanks for your in-depth responses Papa, it is appreciated! I just realised I called move semantics copy... I blame... ahh... something!

RE looking at other vids on C++ topics: I know chili is all for the getting of knowledge, and for me getting different views and explanations helps better place and concrete things in my mind. It's kind of like GPS where the more signals you get the more accurate the estimation of your location is.

The vid I saw recently on rule of three said basically if you have data on the heap you need to define a destructor and the copy stuff. I think it was made before 11 so move wasn't included. I watched one on rvalues which was basically on move stuff with an explanation on how to understand what && means; that was pretty informative.

So the member access level stays the same down the descendant tree? This makes sense and is the simplest way for it to work I think. I need to seek out more of my own learning more before trying to discuss things I think. I'm trying to remember though, I feel there is something where once inherited the access goes private. I guess maybe it was private is not accessible from children but I'm sure it was something that did get inherited that wouldn't come through to the grandchild. I just ran my own test on inheritance, basically what you posted in the end, and then modified it to tinker with multiple & diamond inheritance.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Direct3D Tutorial 8 / has VS is

Post by albinopapa » March 10th, 2019, 4:51 am

You can change the inheritance restrictions:

Code: Select all

class A
{
public:
	virtual int GetValue() { return private_aa; }
	int public_aa = 0;
protected:
	int protected_aa = 0;
private:
	int private_aa = 0;
};
class B : public A
{
public:
	int GetValue()override { return protected_aa; }
	
	void SetPublicBAA( int _value ) { public_aa = _value; }			// Accessable
	void SetProtectedBAA( int _value ) { protected_aa = _value; }	// Accessable
	void SetPrivateBAA( int _value ) { private_aa = _value; }		// Not accessable
	void SetPublicBBA( int _value ) { public_ba = _value; }			// Accessable
	void SetProtectedBBA( int _value ) { protected_ba = _value; }	// Accessable
	void SetPrivateBBA( int _value ) { private_ba = _value; }		// Accessable

public:
	int public_ba = 0;
protected:
	int protected_ba = 0;
private:
	int private_ba = 0;
};
class C : protected B
{
public:
	int GetValue()override { return protected_aa; }

	void SetPublicCAA( int _value ) { public_aa = _value; }			// Accessable
	void SetProtectedCAA( int _value ) { protected_aa = _value; }	// Accessable
	void SetPrivateCAA( int _value ) { private_aa = _value; }		// Not accessable
	void SetPublicCBA( int _value ) { public_ba = _value; }			// Accessable
	void SetProtectedCBA( int _value ) { protected_ba = _value; }	// Accessable
	void SetPrivateCBA( int _value ) { private_ba = _value; }		// Not Accessable
	void SetPublicCCA( int _value ) { public_ca = _value; }			// Accessable
	void SetProtectedCCA( int _value ) { protected_ca = _value; }	// Accessable
	void SetPrivateCCA( int _value ) { private_ca = _value; }		// Accessable

	int public_ca = 0;
protected:
	int protected_ca = 0;
private:
	int private_ca = 0;
};
class D : private C
{
public:
	int GetValue()override { return protected_aa; }		

	void SetPublicCAA( int _value ) { public_aa = _value; }			// Accessable
	void SetProtectedCAA( int _value ) { protected_aa = _value; }	// Accessable
	void SetPrivateCAA( int _value ) { private_aa = _value; }		// Not accessable
	void SetPublicCBA( int _value ) { public_ba = _value; }			// Accessable
	void SetProtectedCBA( int _value ) { protected_ba = _value; }	// Accessable
	void SetPrivateCBA( int _value ) { private_ba = _value; }		// Not Accessable
	void SetPublicCCA( int _value ) { public_ca = _value; }			// Accessable
	void SetProtectedCCA( int _value ) { protected_ca = _value; }	// Accessable
	void SetPrivateCCA( int _value ) { private_ca = _value; }		// Not Accessable
	void SetPublicDCA( int _value ) { public_da = _value; }			// Accessable
	void SetProtectedDCA( int _value ) { protected_da = _value; }	// Accessable
	void SetPrivateDCA( int _value ) { private_da = _value; }		// Accessable

public:
	int public_da = 0;
protected:
	int protected_da = 0;
private:
	int private_da = 0;
};

void Func( A& _a, B& _b, C& _c, D& _d )
{
	A& _ab = _b; // B has public inheritance and can be converted to base class
	A& _ac = _c; // C has protected inheritance and cannot be converted to grandparent class

	B& _bc = _c; // C has protected inheritance and cannot be converted to parent class

	A& _a = _d;  // D has private inheritance and cannot be converted to great-grandparent class
	B& _b = _d;  // D has private inheritance and cannot be converted to grandparent class
	C& _c = _d;  // D has private inheritance and cannot be converted to parent class


	_a.public_aa = 3;	// Accessable because member is public

	_b.public_aa  = 3;	// Accessable because inheritance is public and member is public
	_b.public_ba  = 3;	// Accessable because member is public

	_c.public_aa  = 3;	// Not acessable because C has protected inheritence
	_c.public_ba  = 3;	// Not acessable because C has protected inheritence
	_c.public_ca  = 3;	// Accessable because member is public

	_d.public_aa  = 3;	// Not acessable because D has private inheritance
	_d.public_ba  = 3;	// Not acessable because D has private inheritance
	_d.public_ca  = 3;	// Not acessable because D has private inheritance
	_d.public_da  = 3;	// Accessable because member is public
}
So, with public inheritance, all public methods both virtual or other can be accessed.
With protected and private inheritance, there is not implicit conversion and outside classes or functions cannot access even the inherited public functions.
As you can see, all child classes can access public and protected methods and members of their ancestor classes, but never private members.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply