April 1999
Version: 1.0, 3.0, and 4.0

Using properties in C++ classes

by Kent Reisdorph

It's common knowledge that the VCL uses properties to provide much of its functionality. Properties allow the VCL to do what appears at first to be black magic. For example, when you change the value of the Left property for a form, the form magically moves on the screen. This is because the Left property has a write method associated with it. When you assign a value to the Left property, the write method is called, and code is executed behind the scenes. In this case, the code moves the form on the screen. It's a given that properties work in C++Builder--you use them every time you write a VCL application. But did you know that you can use properties in your own C++ classes? Indeed, you can. This article will explain how.

Properties in C++

The property keyword is intrinsic to the Object Pascal language (the language in which the VCL is written). The C++ language, however, doesn't include the concept of properties. In order to facilitate use of the VCL in C++Builder, Inprise created extensions to the C++ language. One extension is manifest in the __property keyword. This new keyword allows C++ to use properties just as Delphi does. A quick glance at the VCL headers shows you hundreds of declarations with the __property keyword. Using properties in your own C++ classes is extremely easy and can provide an alternative to traditional data hiding in C++. Just about any book that covers OOP will tell you that you shouldn't make your class data members public. Instead, you should write getters and setters. Getters and setters are class member functions that get and set the value of the class' private data members. By using getters and setters, you can control access to your data members (data hiding). Properties provide an alternative, and much cleaner, way of implementing data hiding.

Property basics

Before we explain how to use properties, let's take a brief moment to review the characteristics of properties. First, properties are unable to store any data in and of themselves. Instead, an underlying data member of the class is used to store the property's value. The property is tied to the data member when the property is declared. Second, properties can be read/write, read-only, or, in extremely rare cases, write-only. Third, some properties use direct access to store and retrieve their data. When a property uses direct access, the property is directly tied to the underlying data member. Reading the property simply returns the value of the data member. Writing to the property simply assigns a new value to the underlying data member.

Finally, some properties use read and/or write methods. Read and write methods are methods that are called when the property is read or written to. Let's go back to the example of a form's Left property. The Left property has a write method called SetLeft. This method is called every time the Left property is assigned a value. The SetLeft method includes code to move the form on the screen (among other things). So it's not black magic at work at all, but simply properties in action.

 
Note: Direct access and read/write methods can be mixed in the same property. Many properties use direct access for reading the property value, but a write method for writing to the property.

To summarize, if a property includes a write method, then that method will be called every time the property is written to. If the property includes a read method, then that read method will be called every time the property's value is read.

A class that uses properties

Let's first look at a simple class that emulates a point (x and y coordinates). In straight C++, the class might look like this:
class TMyPoint {
	int X;
	int Y;
};
That's a pretty simple class, so let's take it a bit further. Let's say that your point class only allows a specific range of x and y coordinates. For the sake of argument, let's say that both coordinates must fall in the range of 0 to 100. Using straight C++, you'd write the class like this:
class TMyPoint {
	private:
		int X;
		int Y;
	public:
		void SetX(int x) {
			if (x > 100 || x < 0)
				throw ("Range error");
			else
			X = x;
		}
		void SetY(int y) {
			if (y > 100 || y < 0)
				throw ("Range error");
			else
				Y = y;
		}
		int GetX() { return X; };
		int GetY() { return Y; };
};
As you can see, the class now has private data and uses getter and setter methods to access the data. Note that the setter methods throw an exception if the value passed in is less than 0 or greater than 100. This all works perfectly fine, but the code to make use of this class isn't exactly a thing of beauty:
TMyPoint point;
point.SetX(50);
point.SetY(100);
Label1->Caption = String(point.GetX())
	+ ", " + String(point.GetY());
Now, let's consider this class when written using properties. The new class declaration looks like this:
class TMyPoint {
	private:
		int FX;
		int FY;
		void SetX(int x) {
			if (x > 100 || x < 0)
				throw ("Range error");
			else
				FX = x;
		}
		void SetY(int y) {
			if (y > 100 || y < 0)
				throw ("Range error");
			else
				FY = y;
		}
	public:
		__property int X = {read=FX, 
			write=SetX};
		__property int Y = {read=FY, 
			write=SetY};
};
There are several items of note in our new TMyPoint class. First, notice that the underlying data members are called FX and FY. The use of the letter F preceding the data members is traditional, but you can use any naming convention you like. The important thing to note here, though, is that the underlying data members must use a name other than X and Y because those are our property names.

Next, notice the SetX and SetY property write methods. These methods will be called each time the X and Y properties are assigned a value. The declaration of a write method must conform to a predetermined signature. Specifically, the write method must take a single parameter of the property's type and must return void.

Finally, notice the X and Y property declarations. Here's the X property's declaration again:

__property int X = {read=FX, write=SetX};
This property is declared of type int because the property's data type must exactly match the underlying data member's type. Notice that we're using direct access to read the property (read=FX), and a write method to write to the property (write=SetX). (Note that if you omit the write statement, the property becomes a read-only property.) The code to implement our newly written class now looks like this:

 

TMyPoint point;
point.X = 50;
point.Y = 100;
Label1->Caption =
	String(point.X) + ", " + 
		String(point.Y);
This code is much easier to read and is much cleaner than the previous example using a straight C++ class.

Conclusion

Properties allow you to write cleaner code and to perform some specific action when the property is written to or read. In addition, using write methods for your properties allows you to throw exceptions if invalid values are assigned to your properties. One drawback to using properties in your C++ classes is that properties aren't portable to other platforms. If writing portable code is important to you, then you probably don't want to use properties in your C++ classes. For your VCL applications, however, using properties can enhance the usefulness of your C++ classes.