This module discussed some of the basic concepts of building classes in C++ and were introduced to the internal workings of the
class
construct.
In particular you learned:
- How the scope resolution operator is used to define class scope
- How to write external member functions for a
class
- Some basic concepts of function overloading
- How nested classes work
- The use of
static
and const
members in a class
- How to use the self-referential
this
pointer
- How the Scope Resolution Operator is Used to Define Class Scope
The "scope resolution operator (`::`)"" in C++ is used to define or identify the context in which a name (such as a class member function or variable) exists. When defining member functions outside the class declaration, the scope resolution operator specifies that the function belongs to a particular class.
Usage:**
Defining Member Functions Outside the Class:**
class MyClass {
public:
void myFunction();
};
// Definition outside the class using the scope resolution operator
void MyClass::myFunction() {
// Function implementation
}
Accessing Nested Classes or Enumerations:**
class OuterClass {
public:
class InnerClass {
// Inner class definition
};
};
OuterClass::InnerClass obj; // Using scope resolution to access InnerClass
Key Points:**
- Clarifies Scope:** Helps the compiler understand which class a member belongs to, especially when multiple classes have members with the same name.
- Namespace Management:** Also used to specify the namespace in which a class or function resides.
- How to Write External Member Functions for a Class
Defining member functions outside the class declaration can improve code readability and organization, especially for larger classes.
**Steps to Write External Member Functions:**
- Declare the Function Inside the Class:**
class MyClass {
public:
void myFunction(); // Declaration
};
1. **
2. **
Define the Function Outside the Class Using the Scope Resolution Operator:**
void MyClass::myFunction() {
// Implementation code
}
Benefits:**
- Separation of Interface and Implementation:** Keeps the class declaration clean and focused on the interface.
- Improved Readability:** Easier to navigate through code when implementation details are separate.
Example:**
class Calculator {
public:
int add(int a, int b); // Declaration
};
int Calculator::add(int a, int b) { // Definition
return a + b;
}
- Some Basic Concepts of Function Overloading
"Function overloading" allows you to have multiple functions with the same name but different parameter lists within the same scope. It enables functions to handle different data types or different numbers of parameters.
Key Concepts:**
- Different Signatures:** Overloaded functions must differ in the type and/or number of parameters.
- Compile-Time Resolution:** The compiler determines which function to call based on the arguments provided.
- Cannot Overload by Return Type Alone:** Changing only the return type is insufficient for overloading.
Example:**
class Printer {
public:
void print(int i);
void print(double f);
void print(const std::string& s);
};
void Printer::print(int i) {
std::cout << "Printing int: " << i << std::endl;
}
void Printer::print(double f) {
std::cout << "Printing double: " << f << std::endl;
}
void Printer::print(const std::string& s) {
std::cout << "Printing string: " << s << std::endl;
}
**Usage:**
Printer p;
p.print(5); // Calls print(int)
p.print(3.14); // Calls print(double)
p.print("Hello World"); // Calls print(const std::string&)
- How Nested Classes Work in C++:
A "nested class" is a class defined within the scope of another class. It is useful for logically grouping classes that are closely related and for controlling access to the nested class.
Characteristics:**
- Access to Enclosing Class Members:** By default, the nested class does not have access to the members of the enclosing class.
- Access Control:** The nested class can be declared under `public`, `protected`, or `private` sections to control its accessibility.
- Scope Specification:** To use the nested class outside the enclosing class, you need to specify the full scope.
Example:**
class EnclosingClass {
public:
int outerData;
class NestedClass {
public:
void display(EnclosingClass& obj) {
// Accessing outer class's public member via object reference
std::cout << "Outer Data: " << obj.outerData << std::endl;
}
};
};
int main() {
EnclosingClass outer;
outer.outerData = 10;
EnclosingClass::NestedClass nested;
nested.display(outer);
return 0;
}
**Output:**
Outer Data: 10
Key Points:**
- Instantiation:** You can create an object of the nested class using the scope of the enclosing class.
- Encapsulation:** Nested classes can help encapsulate helper classes that are only relevant to the enclosing class.
- The Use of `static` and `const` Members in a Class
`static` Members:**
- **Shared Across All Instances:** A `static` member variable or function belongs to the class rather than any object instance.
- **Access Without Object:** Can be accessed using the class name directly.
- **Initialization:** Static member variables must be defined and (if not integral types) initialized outside the class.
**Example of `static` Member Variable:**
class Counter {
public:
static int count;
Counter() {
count++;
}
};
int Counter::count = 0; // Definition and initialization
int main() {
Counter c1;
Counter c2;
std::cout << "Count: " << Counter::count << std::endl; // Output: Count: 2
return 0;
}
#### **`const` Members:**
- **Immutable After Initialization:** A `const` member variable cannot be modified after it is initialized.
- **Initialization:** Must be initialized using an initializer list in the constructor.
**Example of `const` Member Variable:**
class Immutable {
public:
const int id;
Immutable(int i) : id(i) {} // Initialization in constructor
};
int main() {
Immutable obj(10);
// obj.id = 20; // Error: assignment of read-only member
std::cout << "ID: " << obj.id << std::endl; // Output: ID: 10
return 0;
}
**`const` Member Functions:**
- **Promise Not to Modify the Object:** Declared with `const` at the end of the function signature.
- **Can Be Called on `const` Objects:** Only `const` member functions can be called on objects that are `const`.
**Example of `const` Member Function:**
class Data {
public:
int value;
Data(int v) : value(v) {}
int getValue() const {
return value;
}
void setValue(int v) {
value = v;
}
};
int main() {
const Data obj(5);
std::cout << obj.getValue() << std::endl; // Allowed
// obj.setValue(10); // Error: cannot call non-const function on const object
return 0;
}
- How to Use the Self-Referential `this` Pointer:
The `this` pointer is an implicit parameter in all non-static member functions. It points to the object for which the member function is called.
Uses of `this` Pointer:**
- Accessing Members:** Useful when parameter names hide member variables.
- Method Chaining:** Allows functions to return the object's reference.
- Passing the Object:** Can pass the current object to other functions or methods.
Example of Resolving Naming Conflicts:
class Sample {
private:
int value;
public:
void setValue(int value) {
this->value = value; // 'this->value' refers to the member variable
}
};
**Example of Method Chaining:**
class Number {
private:
int value;
public:
Number(int v) : value(v) {}
Number& increment() {
value++;
return *this;
}
void display() {
std::cout << "Value: " << value << std::endl;
}
};
int main() {
Number num(5);
num.increment().increment().display(); // Chaining method calls
// Output: Value: 7
return 0;
}
Key Points:**
- Type of `this`:** Inside a non-const member function of class `C`, `this` is of type `C* const`.
- In `const` Member Functions:** In a `const` member function, `this` is of type `const C* const`.
By understanding and utilizing these concepts, you can effectively build and manage classes in C++, leveraging the language's powerful object-oriented features to write clean, efficient, and maintainable code.
You define a new data type by defining a class, but before I get into the language, syntax, and programming techniques of classes, I will explain how your existing knowledge relates to the concept of object-oriented programming.
Almost everything you have seen up to now has been procedural programming, which involves programming a solution in terms of fundamental data types.
The essence of object-oriented programming is that you write programs in terms of objects in the domain of the problem you are trying to solve, so part of the program development process involves designing a set of types to suit the problem context.
If you are writing a program to keep track of your bank account, you will probably need to have data types such as Account and Transaction.
For a program to analyze baseball scores, you may have types such as Player and Team. The variables of the fundamental types do not allow you to model real-world objects (or even imaginary objects) very well. It is not possible to model a baseball player realistically in terms of just an int or double, value or any other fundamental data type. You need several values of a variety of types for any meaningful representation of a baseball player.
Classes provide a solution. A class type can be
- a composite of variables of other types of fundamental types or
- of other class types.
A class can also have functions as an integral part of its definition. You could define a class type called Box that contains variables that store a length, a width, and a height to represent boxes. You could then define variables of type Box, just as you define variables of
fundamental types.
Each Box object would contain its own length, width and height dimensions and you could create and manipulate as many Box objects as you need in a program.