Enhanced Constructor Mechanics
The initialization list provides an alternative to assigning values within the constructor body. This approach begins with a colon followed by a comma-separated list of member variables paired with their initial values in parentheses.
class DateTime
{
public:
DateTime(int year, int month, int day)
: m_year(year),
m_month(month),
m_day(day)
{}
private:
int m_year;
int m_month;
int m_day;
};
Each member can only appear once in the initialization list since this represents their definition point. Certain member types require initialization through the list: references, constants, and objects without default constructors.
class Clock
{
public:
Clock(int hour_value) : m_hour(hour_value)
{
std::cout << "Clock created" << std::endl;
}
private:
int m_hour;
};
class Calendar
{
public:
Calendar(int& ref_val, int year, int month, int day)
: m_year(year),
m_month(month),
m_day(day),
m_ref(ref_val),
m_constant(42),
m_clock_obj(12)
{}
private:
int m_year;
int m_month;
int m_day;
int& m_ref;
const int m_constant;
Clock m_clock_obj;
};
C++11 introduced default member initializers at declaration, which apply when members aren't explicitly listed in the initialization list.
class Calendar
{
public:
Calendar(int year, int month)
: m_year(year),
m_month(month)
// m_day will use default value of 9
{}
int m_year = 5;
int m_month = 6;
int m_day = 9;
};
Initialization follows the declaration order in the class, regardless of the sequence in the initialization list.
Type Conversion Mechanisms
C++ permits implicit conversion from built-in types to class objects when appropriate single-parameter constructors exist.
class NumericValue
{
public:
NumericValue(int value) : m_data(value) {}
void display() { std::cout << m_data << std::endl; }
private:
int m_data;
int m_default = 100;
};
int main()
{
// Implicit conversion creates temporary object
NumericValue obj = 42;
obj.display();
// Multi-parameter conversion with braces
NumericValue multi_obj{10, 20}; // Requires brace initialization
return 0;
}
Adding explicit to the constructor prevents implicit conversions:
explicit NumericValue(int value) : m_data(value) {}
Static Components
Static data members belong to the class rather than individual instances and must be defined outside the class:
class InstanceCounter
{
public:
InstanceCounter() { ++m_instance_count; }
InstanceCounter(const InstanceCounter&) { ++m_instance_count; }
~InstanceCounter() { --m_instance_count; }
static int get_count()
{
return m_instance_count;
// Cannot access non-static members due to absence of 'this'
}
private:
static int m_instance_count;
int m_value;
};
// Definition outside class required
int InstanceCounter::m_instance_count = 0;
int main()
{
std::cout << InstanceCounter::get_count() << std::endl; // Access via scope resolution
InstanceCounter item1, item2;
std::cout << item1.get_count() << std::endl; // Also valid
return 0;
}
Friand Functions and Classes
Friends provide access to private and protected members while existing outside the class hierarchy:
class DataContainer
{
public:
friend void external_access(const DataContainer& container);
DataContainer(int val1 = 10, int val2 = 20)
: m_first(val1), m_second(val2) {}
private:
int m_first = 10;
int m_second = 20;
};
void external_access(const DataContainer& container)
{
std::cout << container.m_first << std::endl;
std::cout << container.m_second << std::endl;
}
// Friend class relationship
class ContainerManager
{
private:
static int m_static_var;
int m_private_data = 100;
public:
class DataProcessor // Automatically a friend of ContainerManager
{
public:
void process(const ContainerManager& mgr)
{
std::cout << m_static_var << std::endl; // Access to static
std::cout << mgr.m_private_data << std::endl; // Access to private
}
};
};
int ContainerManager::m_static_var = 50;
Friend relationships are unidirectional and don't support transitivity.
Nested Classes
Nested classes operate independently but have special access privileges:
class OuterClass
{
private:
static int m_shared_value;
int m_instance_data = 42;
public:
class InnerClass
{
public:
void examine(const OuterClass& outer)
{
std::cout << m_shared_value << std::endl; // Direct access to static
std::cout << outer.m_instance_data << std::endl; // Direct access to instance
}
};
};
int OuterClass::m_shared_value = 123;
int main()
{
std::cout << sizeof(OuterClass) << std::endl; // Inner class doesn't affect size
OuterClass::InnerClass processor;
OuterClass outer_instance;
processor.examine(outer_instance);
return 0;
}
Temporary Objects
Temporary objects created with Type(args) syntax exist only for the duration of their statement:
class TemporaryExample
{
public:
TemporaryExample(int val = 0) : m_value(val)
{
std::cout << "Created with " << m_value << std::endl;
}
~TemporaryExample()
{
std::cout << "Destroyed" << std::endl;
}
private:
int m_value;
};
int main()
{
TemporaryExample(100); // Object created and destroyed immediately
// Destructor called at end of this line
return 0;
}