Typically, when designing classes, the goal is to ensure that each instance of the class operates independently. This means that modifications to one instance should not impact another. However, there are scenarios where we may need to have data shared across all instances of a class. This could involve accessing a common global resource or executing initialization logic when the first instance is created. In such cases, C++ offers the static keyword to define class-specific information that is shared among all instances of that class. Similar to const, the static keyword comes with its own intricacies and may not always align with intuition. Hence, this guide delves into static member functions, their interaction with const, and static data members.
There are two methods to utilize static keywords in C++. These include:
- Static Data Members
- Static Member Functions
Static Data Member
Static data members are data members that are defined with the "static" keyword.
Whenever a data member is declared as static, whether within or outside a class, it is referred to as a static data member. Unlike regular data members, there is a single instance of a static data member regardless of how many class objects are created. By default, a static data member is initialized to zero. This unique property of static data members allows them to be shared among all instances of the class while retaining their values.
Syntax
static data_type data_member_name:
In simpler terms, static data members are variables specific to a class that all instances of the class can access. When there are several instances of a class, they all utilize the identical instance of the variable, meaning modifications to static data members impact all objects. The way to declare static data members can be perplexing because it involves two separate stages - declaration and definition. Let's consider a scenario with the given class definition:
class MyClass
{
public:
/* Omitted */
private:
static int myStaticData;
};
Above, myStaticData has defined a static data member using the static keyword. Nonetheless, the declaration static int myStaticData does not actually instantiate the variable myStaticData. Its purpose is to indicate to the compiler that a variable named myStaticData will be defined later. Therefore, in order to assign a value to myStaticData, it is necessary to include an additional line in our code similar to the following:
int MyClass::myStaticData = 137;
There are several key points to highlight in these importancpp tutorials. Initially, it is crucial to utilize the fully-qualified name MyClass::myStaticData when declaring the variable, rather than solely using myStaticData. Secondly, it is important not to redundantly use the static keyword in the variable declaration to avoid confusion for the compiler. Lastly, despite myStaticData being defined as private, it is permissible to declare it outside the class definition. The rationale behind this dual declaration/definition approach for static data is somewhat intricate, revolving around how the compiler assigns storage for variables. At this stage, it is essential to understand that static data necessitates separate declaration and definition to function correctly.
A data member in a class can be declared static. A static data member has certain special characteristics. These are:
- Only one copy of that member is created for the entire class and shared by all the class objects, also called a class data member.
- It must be declared as a private data member.
- It can be accessed not only by the object name but also by the class name.
- It is initialized to zero when the first object of its class is created.
#include <iostream>
using namespace std;
class MyClass{
public:
int x;
static int count;
//default constructor
MyClass()
{
count++;
}
};
//initialization
int MyClass::count=0;
int main(){
cout<<"Initial Count:"<<MyClass::count<<endl;
MyClass obj1;
cout<<"Count after one object:"<<MyClass::count<<endl;
return 0;
}
Static data members appear similar to regular data members in nearly all aspects except for their declaration. Take, for example, the member function named doSomething within the class MyClass:
void MyClass::doSomething()
{
myStaticData ++; // Modifies myStaticData for all classes
}
This scenario appears quite typical, and this code is perfectly functional. It is essential to be aware that altering myStaticData impacts a variable that other MyClass instances may access. Therefore, it is crucial to restrict the use of static data to situations where the information is not unique to any particular class.
Static Member Functions
If a class member function is defined as a static member function, it can only access static data members. Additionally, this function can be accessed even without an instance of the class being created.
Member Functions
Defining Member Function
Member functions are able to be declared in two different locations: within the class definition itself, or outside of the class definition.
A member function within a class has the option to be defined as static. When a member function is declared as static, it possesses unique traits that distinguish it from non-static member functions:
- Static functions are limited to accessing only other static members within the class.
- Both the object name and the class name have the capability to invoke a static member function.
Within member methods, a unique variable known as this functions as a reference to the current object. When you interact with instance variables, you are essentially interacting with the instance variables of this reference.
How does C++ know what value this refers to?
The solution is nuanced yet significant. Let's consider a scenario where we define a class named MyClass containing a method called doSomething that takes in two integer arguments. Each time we call the doSomething function on an instance of MyClass using the following format: myInstance.doSomething(a, b);
Member function inside the class
- Member functions inside the class can be declared in public (or) private sections.
- The member function defined inside the class is treated as an inline function.
- The member functions are defined inside the class when small; otherwise, they should be defined outside the class.
Code for the member function declaration
#include <iostream>
using namespace std;
class Rectangle {
public:
int length; // Length of a box
int breadth; // Breadth of a box
int height; // Height of a box
//Declaration of member functions
int getVolume(void);
void setLength( int len );
void setBreadth( int bre );
void setHeight( int hei );
};
// Definition of Member functions
int Rectangle::getVolume(void) {
return length * breadth * height;
}
void Rectangle::setLength( int len ) {
length = len;
}
void Rectangle::setBreadth( int bre ) {
breadth = bre;
}
void Rectangle::setHeight( int hei ) {
height = hei;
}
// Main function of the program
int main() {
Rectangle Rectangle1; // Declare Box1 of type Box
Rectangle Rectangle2; // Declare Box2 of type Box
int volume = 0; // Store the volume of a box here
// rectangle 1 specification
Rectangle1.setLength(3);
Rectangle1.setBreadth(4);
Rectangle1.setHeight(5);
// rectangle 2 specification
Rectangle2.setLength(1);
Rectangle2.setBreadth(2);
Rectangle2.setHeight(3);
// volume of rectangle 1
volume = Rectangle1.getVolume();
cout << "Volume of Rectangle1 : " << volume <<endl;
// volume of rectangle 2
volume = Rectangle2.getVolume();
cout << "Volume of Rectangle2 : " << volume <<endl;
return 0;
}
Output:
Explanation
In the program mentioned, the functions getdata and display are implemented within the class under the public section. Within the main function, an instance named one is created. It is important to note that objects have access to the public members of a class. The object one calls the public function getdata to set the values and then uses display to showcase the output.
doSomething(&myInstance, a, b);
Where doSomething is prototyped as
Perform a task using the MyClass object, with parameters 'a' and 'b' passed as arguments.
For the most part, this nuanced difference may not be a primary concern for you as a developer. Nonetheless, the scenario where an N-argument member function transforms into an N+1-argument-free function can lead to challenges in certain scenarios. For instance, consider a situation where you are creating a class named Point structured as follows:
class Point
{
public:
bool compareTwoPoints(const Point &one, const Point &two)
{
if(one.x < two.x)
return true;
if(one.x > two.x)
return false;
return one.y < two.y;
}
private:
int x, y;
};
If we want to provide a vector for the STL sort function, we may encounter difficulties if we attempt to utilize this specific syntax:
Arrange the elements in the myVector container using the sort function, with the comparison function &Point::compareTwoPoints.
The issue arises from the requirement of the sort function for a two-parameter function returning a boolean value. In this scenario, the function we've supplied contains three parameters: two points for comparison and an implicit "this" pointer. As a result, the code mentioned will trigger an error.
Member function outside the class
When a function is extensive, it is preferable to define a member function outside the class. To do this, the following guidelines need to be adhered to:
- Firstly, the function prototype must be declared within the class.
- Next, the function name along with its return type must be preceded by the class name, separated by the scope resolution operator(::).
Syntax
return-type class-name :: function-name (argument declaration)
{
//function body;
}
The subsequent instance demonstrates the function that is declared external to the class.
#include <iostream>
using namespace std;
class MyExample
{
private:
int val;
public:
//function declarations
void init_val(int v);
void print_val();
};
//function definitions
void MyExample::init_val(int v)
{
val=v;
}
void MyExample::print_val()
{
cout<<"val: "<<val<<endl;
}
int main()
{
//create the object
MyExample Ex;
Ex.init_val(32);
Ex.print_val();
return 0;
}
Output:
Characteristics of Member Function
- The difference between the member and normal function is that the normal function can be invoked freely, whereas the member function only uses an object of the same class.
- The same function can be used in any number of classes. It is possible because the function's scope is limited to their classes and cannot overlap one another.
- The public member function can access the private data or private function. Other functions have no access permission.
- The member function can invoke one another without using an object or dot operator.