Memory Partitioning in C++
When a C++ program executes, memory is divided into four main areas:
- Code Area: Stores binary code of functions, managed by the operating system.
- Global Area: Stores global variables, static variables, and constants.
- Stack Area: Automatically allocated and released by the compiler; stores function parameters, local variables, etc.
- Heap Area: Allocated and released by the programmer; if not released, the operating system reclaims it at program termination.
Significance of Memory Partitioning: Data in different areas has different lifetimes, providing greater flexibility in programming.
1.1 Before Program Execution
After compilation, an executable (e.g., .exe on Windows) is generated. Before execution, memory is divided into two areas:
- Code Area:
- Contains machine instructions executed by the CPU.
- Shared: Only one copy of frequently executed code is kept in memory.
- Read-only: Prevents accidental modification of instructions.
- Global Area:
- Stores global variables and static variables.
- Also contains the constant area, which holds string constants and other constants.
- Data in this area is released by the OS after program termination.
Example:
#include <iostream>
using namespace std;
// Global variables
int globalA = 10;
int globalB = 10;
// Global constants
const int constGlobalA = 10;
const int constGlobalB = 10;
int main() {
// Local variables
int localA = 10;
int localB = 10;
// Printing addresses
cout << "Local variable localA address: " << (intptr_t)&localA << endl;
cout << "Local variable localB address: " << (intptr_t)&localB << endl;
cout << "Global variable globalA address: " << (intptr_t)&globalA << endl;
cout << "Global variable globalB address: " << (intptr_t)&globalB << endl;
// Static variables
static int staticA = 10;
static int staticB = 10;
cout << "Static variable staticA address: " << (intptr_t)&staticA << endl;
cout << "Static variable staticB address: " << (intptr_t)&staticB << endl;
// String constants
cout << "String constant address: " << (intptr_t)&"hello world" << endl;
cout << "String constant address: " << (intptr_t)&"hello world1" << endl;
cout << "Global constant constGlobalA address: " << (intptr_t)&constGlobalA << endl;
cout << "Global constant constGlobalB address: " << (intptr_t)&constGlobalB << endl;
const int constLocalA = 10;
const int constLocalB = 10;
cout << "Local constant constLocalA address: " << (intptr_t)&constLocalA << endl;
cout << "Local constant constLocalB address: " << (intptr_t)&constLocalB << endl;
system("pause");
return 0;
}
Summary:
- Before program execution, memory is divided into the global area and code area.
- Code area: shared and read-only.
- Global area stores global variables, static variables, and constants.
- Constant area stores constants modified by
constand string constants.
1.2 After Program Execution
Stack Area
- Automatically allocated and released by the compiler; stores function parameters, local variables, etc.
- Note: Do not return the address of a local variable. The compiler automatically releases stack data.
Example:
int* func() {
int a = 10;
return &a; // Dangerous: returning address of local variable
}
int main() {
int* p = func();
cout << *p << endl; // Undefined behavior
cout << *p << endl;
system("pause");
return 0;
}
Heap Area
- Allocated and released by the programmer; if not released, the OS reclaims it at program termination.
- In C++, use
newto allocate memory on the heap.
Example:
int* func() {
int* a = new int(10);
return a;
}
int main() {
int* p = func();
cout << *p << endl;
cout << *p << endl;
delete p; // Release memory
system("pause");
return 0;
}
Summary:
- Heap data is managed by the programmer using
newto allocate anddeleteto release.
1.3 The new Operator
- Use
newto allocate data on the heap. - Dat a allocated with
newmust be released manually usingdelete. - Syntax:
new dataType - Returns a pointer to the allocated data.
Example 1: Basic Syntax
int* func() {
int* a = new int(10);
return a;
}
int main() {
int* p = func();
cout << *p << endl;
cout << *p << endl;
delete p; // Release memory
// cout << *p << endl; // Error: accessing freed memory
system("pause");
return 0;
}
Example 2: Allocating Arrays
int main() {
int* arr = new int[10];
for (int i = 0; i < 10; ++i) {
arr[i] = i + 100;
}
for (int i = 0; i < 10; ++i) {
cout << arr[i] << endl;
}
// Release array with delete[]
delete[] arr;
system("pause");
return 0;
}
References
2.1 Basic Usage of References
Purpose: Create an alias for a variable.
Syntax: dataType &alias = originalName;
Example:
int main() {
int a = 10;
int &b = a;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
b = 100;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
2.2 Notes on References
- References must be initialized.
- Once initialized, a reference cannot be changed to refer to another variable.
Example:
int main() {
int a = 10;
int b = 20;
// int &c; // Error: must be initialized
int &c = a; // c is now an alias for a
c = b; // This assigns b's value to a, but c still refers to a
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
return 0;
}
2.3 References as Function Parameters
Purpose: Modify actual parameters via references in function calls. Advantage: Simplifies pointer usage.
Example:
// Pass by value
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
}
// Pass by address
void swapByAddress(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Pass by reference
void swapByRef(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10, b = 20;
swapByValue(a, b); // a and b unchanged
cout << "a:" << a << " b:" << b << endl;
swapByAddress(&a, &b); // a and b swapped
cout << "a:" << a << " b:" << b << endl;
swapByRef(a, b); // a and b swapped again
cout << "a:" << a << " b:" << b << endl;
return 0;
}
Summary: Reference parameters produce the same effect as address passsing, but with cleaner syntax.
2.4 References as Function Return Values
Purpose: Return a reference from a function. Note: Do not return a reference to a local variable. Usage: A function call that returns a reference can be used as an lvalue.
Example:
// Returning reference to local variable (unsafe)
int& test01() {
int a = 10; // local
return a;
}
// Returning reference to static variable
int& test02() {
static int a = 20;
return a;
}
int main() {
// Cannot return reference to local variable
int& ref = test01();
cout << "ref = " << ref << endl; // Undefined behavior
cout << "ref = " << ref << endl;
int& ref2 = test02();
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
test02() = 1000; // Function call as lvalue
cout << "ref2 = " << ref2 << endl; // 1000
cout << "ref2 = " << ref2 << endl;
return 0;
}
2.5 The Essence of References
Essence: Internally, a reference is implemented as a constant pointer (Type* const).
Explanation:
// When the compiler sees a reference, it converts it to: int* const ref = &a;
void func(int& ref) {
ref = 100; // The compiler does: *ref = 100;
}
int main() {
int a = 10;
int& ref = a; // Automatically: int* const ref = &a; (pointer constant, pointing cannot change)
ref = 20; // Internally: *ref = 20;
cout << "a:" << a << endl;
cout << "ref:" << ref << endl;
func(a);
return 0;
}
Conclusion: C++ recommends references for syntactic convenience. The essence is a constant pointer, but the compiler handles all pointer operations.
2.6 Constant References
Purpose: Modify formal parameters to prevent unintended modifications.
Add const to function parameters to protect actual arguments.
Example:
void showValue(const int& v) {
// v += 10; // Error: v is const
cout << v << endl;
}
int main() {
// int& ref = 10; // Error: reference requires a modifiable lvalue
const int& ref = 10; // OK: compiler creates temporary: int temp = 10; const int& ref = temp;
// ref = 100; // Error: ref is const
cout << ref << endl;
int a = 10;
showValue(a); // a is not modified
return 0;
}
Function Enhancements
3.1 Default Parameters
Functions can have default values for parameters.
Syntax: returnType functionName(parameter = defaultValue) {}
Example:
int add(int a, int b = 10, int c = 10) {
return a + b + c;
}
// If a parameter has a default value, all subsequent parameters must have defaults.
// If a function declaration has defaults, the definition cannot have them.
int func2(int a = 10, int b = 10);
int func2(int a, int b) {
return a + b;
}
int main() {
cout << "result = " << add(20, 20) << endl; // 50
cout << "result = " << add(100) << endl; // 120
return 0;
}
3.2 Placeholder Parameters
Parameters can be placeholders; they must be provided when calling.
Syntax: returnType functionName(dataType) {}
Example:
void func(int a, int) { // Second parameter is placeholder
cout << "this is func" << endl;
}
int main() {
func(10, 10); // Must provide argument for placeholder
return 0;
}
3.3 Function Overloading
3.3.1 Overview
Purpose: Functions with the same name but different parameters improve reusability. Conditions for overloading:
- Same scope
- Same function name
- Different parameter types, number, or order
Note: Return type alone cannot differentiate overloaded functions.
Example:
void func() {
cout << "func() called" << endl;
}
void func(int a) {
cout << "func(int a) called" << endl;
}
void func(double a) {
cout << "func(double a) called" << endl;
}
void func(int a, double b) {
cout << "func(int a, double b) called" << endl;
}
void func(double a, int b) {
cout << "func(double a, int b) called" << endl;
}
/*
int func(double a, int b); // Not allowed: only return type differs
*/
int main() {
func();
func(10);
func(3.14);
func(10, 3.14);
func(3.14, 10);
return 0;
}
3.3.2 Notes on Function Overloading
- References as overloading condition:
int&vsconst int&are different. - Overloading with default parameters can cause ambiguity.
Example:
void func(int &a) {
cout << "func(int &a) called" << endl;
}
void func(const int &a) {
cout << "func(const int &a) called" << endl;
}
void func2(int a, int b = 10) {
cout << "func2(int a, int b = 10) called" << endl;
}
void func2(int a) {
cout << "func2(int a) called" << endl;
}
int main() {
int a = 10;
func(a); // Calls non-const version
func(10); // Calls const version (rvalue binds to const reference)
// func2(10); // Ambiguous: matches both overloads (default parameter / single parameter)
return 0;
}
Classes and Objects
C++ object-oriented programming has three main characteristics: encapsulation, inheritance, polymorphism. C++ views everything as an object, which has attributes and behaviors.
Example:
- A person object: attributes like name, age, height; behaviors like walking, runnning, eating.
- A car object: attributes like tires, steering wheel; behaviors like carrying people, playing music.
Classes abstract objects with the same properties.
4.1 Encapsulation
4.1.1 Meaning of Encapsulation
Encapsulation is one of the three OOP features in C++. Meaning:
- Combine attributes and behaviors into a single unit to represent real-world entities.
- Control access to attributes and behaviors through access specifiers.
Syntax: class ClassName { accessSpecifier: attributes / behaviors };
Example 1: Circle class to calculate circumference
const double PI = 3.14;
class Circle {
public: // Public access
int radius; // Attribute: radius
double getCircumference() { // Behavior: calculate circumference
return 2 * PI * radius;
}
};
int main() {
Circle c1;
c1.radius = 10;
cout << "Circumference: " << c1.getCircumference() << endl; // 62.8
return 0;
}
Example 2: Student class
class Student {
public:
void setName(string name) { m_name = name; }
void setID(int id) { m_id = id; }
void show() {
cout << "Name: " << m_name << " ID: " << m_id << endl;
}
public:
string m_name;
int m_id;
};
int main() {
Student stu;
stu.setName("Garen");
stu.setID(250);
stu.show();
return 0;
}
Access Specifiers:
public: Acessible inside and outside the class.protected: Accessible inside the class and in derived classes.private: Accessible only inside the class.
Example:
class Person {
public:
string m_Name; // public
protected:
string m_Car; // protected
private:
int m_Password; // private
public:
void func() {
m_Name = "Zhang San";
m_Car = "Tractor";
m_Password = 123456;
}
};
int main() {
Person p;
p.m_Name = "Li Si"; // OK
// p.m_Car = "Benz"; // Error: protected
// p.m_Password = 123; // Error: private
return 0;
}
4.1.2 Difference Between struct and class
The only difference is default access level:
struct: default ispublicclass: default isprivate
class C1 {
int m_A; // private by default
};
struct C2 {
int m_A; // public by default
};
int main() {
C1 c1;
// c1.m_A = 10; // Error: private
C2 c2;
c2.m_A = 10; // OK
return 0;
}