The purpose of "const"?

Maniac

Apolyton Sage
Joined
Nov 27, 2004
Messages
5,588
Location
Gent, Belgium
I want to add a new function to the SDK. However I'm not sure if I need to declare one of the inputs of the functions as a const or not. I don't really understand the purpose of const's applied practically (despite reading wikipedia).

Can forgetting to make something a const, or needlessly saying something is a const, cause crashes?? Or am I worrying about nothing?

In any case, onwards to my actual situation:

I'm wondering if, in CvPlot.h, I should say:

Code:
	int getSpawnValue(CvCity* pCity) const;
or rather
Code:
	int getSpawnValue(const CvCity* pCity) const;

I have seen both kinds in CvPlot.h.

For the record, this function is called in CvCity.cpp like this in a doFungalBloom() function I'm creating:
Code:
iValue = pLoopPlot->getSpawnValue(this);

And the actual function in CvPlot.cpp is

Code:
int CvPlot::getSpawnValue(CvCity* pCity) const
{
	iValue = getSorenRandNum(50, "Spawn Plot Selection")
	if (!isUnit())
	{
		if (getOwner() == pCity->getOwnerINLINE())
		{
			iValue += 220;
		}
		if (getFeatureType() != NO_FEATURE && GC.getFeatureInfo(getFeatureType()).getPlanetValue100() > 0)
		{
			iValue += 110;
		}
		if (isBeingWorked())
		{
			iValue += 50;
		}
	}
	else
	{
		CLLNode<IDInfo>* pUnitNode = headUnitNode();
		while (pUnitNode != NULL)
		{
			CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
			pUnitNode = nextUnitNode(pUnitNode);

			if (getOwnerINLINE() == BARBARIAN_PLAYER)
			{
				if (getOwner() == pCity->getOwnerINLINE())
				{
					iValue += 220;
				}
				if (getFeatureType() != NO_FEATURE && GC.getFeatureInfo(getFeatureType()).getPlanetValue100() > 0)
				{
					iValue += 110;
				}
				if (isBeingWorked())
				{
					iValue += 50;
				}
			}
		}
	}
	return iValue
}

Does that pUnitNode stuff look like it'll actually work btw? I want to loop through all units on that plot. I have no theoretical understanding whatsoever about C++. I'm just copying what I saw elsewhere and vaguely looked right.
 
I want to add a new function to the SDK. However I'm not sure if I need to declare one of the inputs of the functions as a const or not. I don't really understand the purpose of const's applied practically (despite reading wikipedia).

Can forgetting to make something a const, or needlessly saying something is a const, cause crashes?? Or am I worrying about nothing?

In any case, onwards to my actual situation:

I'm wondering if, in CvPlot.h, I should say:

Code:
	int getSpawnValue(CvCity* pCity) const;
or rather
Code:
	int getSpawnValue(const CvCity* pCity) const;

I have seen both kinds in CvPlot.h.

For the record, this function is called in CvCity.cpp like this in a doFungalBloom() function I'm creating:
Code:
iValue = pLoopPlot->getSpawnValue(this);

And the actual function in CvPlot.cpp is

Code:
int CvPlot::getSpawnValue(CvCity* pCity) const
{
	iValue = getSorenRandNum(50, "Spawn Plot Selection")
	if (!isUnit())
	{
		if (getOwner() == pCity->getOwnerINLINE())
		{
			iValue += 220;
		}
		if (getFeatureType() != NO_FEATURE && GC.getFeatureInfo(getFeatureType()).getPlanetValue100() > 0)
		{
			iValue += 110;
		}
		if (isBeingWorked())
		{
			iValue += 50;
		}
	}
	else
	{
		CLLNode<IDInfo>* pUnitNode = headUnitNode();
		while (pUnitNode != NULL)
		{
			CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
			pUnitNode = nextUnitNode(pUnitNode);

			if (getOwnerINLINE() == BARBARIAN_PLAYER)
			{
				if (getOwner() == pCity->getOwnerINLINE())
				{
					iValue += 220;
				}
				if (getFeatureType() != NO_FEATURE && GC.getFeatureInfo(getFeatureType()).getPlanetValue100() > 0)
				{
					iValue += 110;
				}
				if (isBeingWorked())
				{
					iValue += 50;
				}
			}
		}
	}
	return iValue
}

Does that pUnitNode stuff look like it'll actually work btw? I want to loop through all units on that plot. I have no theoretical understanding whatsoever about C++. I'm just copying what I saw elsewhere and vaguely looked right.

Finally! A question that is right up my alley! SWEET!.

Yes, I can answer this question!!!!!

This bit of code indicates that code-writer intends that you can call the "getSpwanValue" method on a CvPlot object without "mutating" the CvPlot object.

Code:
	int CvPlot::getSpawnValue(CvCity* pCity) const;

So, if "cvPlot1" is an object of type CvPlot, then this call

Code:
       cvPlot1->getSpawnValue(cvCity1);

should not change the data fields of cvPlot1. Now, there is a difference between "bitwise" const-ness (enforced by the compiler) and "logical" const-ness (which cannot be enforced by a compiler), but you shouldn't have to worry about this unless you're really advanced. If you are advanced enough to wonder about this, I recommend "Effective C++, Third Ed" by Scott Meyers and see page 21.

But for now, we'll just pretend that "bitwise" const-ness (enforced by the compiler) is all we care about. Basically, a method declared "const", as in our example, cannot change any of the data of the object. If, in our example, cvPlot1 has a field called 'x' that has a value of 3, then after a call to "getSpawnValue", 'x' must still have the value of 3. Does that make sense?

Now, you can also use const on a parameter. When you do this, you're saying that, more or less, that parameter must be used in a read only fashion. You cannot change its data fields in the call to that function. In fact, you can, more or less, only use the methods for that object that are declared "const".

So, in this code:


Code:
	int CvPlot::getSpawnValue(const CvCity* pCity);

The method "getSpawnValue" can only call methods on the "CvCity" object that are declared "const". Notice that I removed the const-ness of the getSpawnValue itself. I did so for clarity. You don't have to have the method "const" for the parameters to be "const". So, if the CvCity class has a method "getSomeValue1" and "getSomeValue2" delcared thus:

Code:
        int CvCity::getSomeValue1();
        int CvCity::getSomeValue2() const;
[code]

Then in the body of "getSpwanValue(const CvCity* pCity)", you could only call "pCity->getSomeValue2()". To be able to call "getSomeValue1", you would have to remove the const on the parameter.

As to your question about if "const" issues can cause crashes, the answer is: not usually. Usually, "const" causes problems in compilation, but makes things better at run-time. However, C++ is such a weird beast, I'm not going to rule it out completely. 

When should you use "const"? At least one expert (Scott Meyers) suggest you use it as often as you can (again, the book is wonderful).

But one practical thing I can say is that most "get" methods could be "const". Usually, they aren't changing state when they are called. However, a quick example of a method that can be const would be a method that has to calculate some value and uses a cache to speed up the process. For example, suppose you had some method called "getAverageSomething" that queried a bunch of objects and did a computationally expensive operation on that data. You can speed up the function by providing a "cache" where the object stores the computed value and only recomputes it when the data has changed. However, such a cache will make const-ing the object more difficult. If the cached value is stored in the object on which "getAverageSomthing" is being called, the method cannot be declared "const".

Does that help? I'd be happy to explain further and provide more examples if you'd like.

-- SJN
 
This version:
Code:
	int getSpawnValue(CvCity* pCity) const;
Means that the function getSpawnValue will not modify the plot object on which it is called but it can do what it wants with the pCity object.

While the other:
Code:
	int getSpawnValue(const CvCity* pCity) const;
Means that not only will this member function preserve the state of the plot object it's being called on, but also that it cannot modify the pCity object either. As a consequence, it can only use const methods on pCity so, for example, pCity->getOwnerINLINE() is okay but pCity->updateCommerce() wouldn't compile because CvCity::updateCommerce() is not a const method.

If you believe that your function should not be modifying the pCity object and want to "guarantee" it, then go with the latter.

The use (or non-use) of const in and of itself should not cause any crashes though as it is basically a compile-time concern rather than a run-time one. It is a way to limit what can be done with or within the function; by using it you are declaring (and hopefully enforcing) your intentions.

[Ninja Edit] Looks like SJN was quicker, but I'm posting anyhow :p
 
Thanks, that was very illuminating. :goodjob:

I guess I'll use const CvCity* pCity then. The only thing being used is the city's owner anyway.
 
Top Bottom