- C++ Keywords (C++98)
Building upon the 32 keywords provided by C, the C++98 standard introduced a total of 63 keywords. These new keywords facilitate object-oriented programming, exception handling, template metaprogramming, and more. While C++ is largely backward compatible with C, it introduces several crucial concepts that resolve many of C's design limitations regarding scope, function logic, and type safety.
- Namespaces
2.1 The Need for Namespaces
In large projects, naming collisions frequently occur when multiple libraries or modules define variables or functions with the same identifier in the global scope. C++ introduces the namespace keyword to localize identifier names and prevent such pollution.
#include <cstdlib>
int count = 42; // Conflict with existing global names
int main() {
printf("%d\n", count); // Ambiguity or redefinition error
return 0;
}
2.2 Defining a Namespace
A namespace is defined using the namespace keyword followed by a name and a block enclosed in curly braces. Namespaces can contain variables, functions, and types. They also support nesting and can be split across multiple files (the compiler will merge them).
namespace MyEngine {
int config_id = 100;
int computeSum(int x, int y) {
return x + y;
}
struct Node {
Node* next;
int data;
};
// Nested namespace
namespace Math {
int subtract(int x, int y) { return x - y; }
}
}
2.3 Using a Namespace
There are three primary ways to access members within a namespace:
- Scope Resolution Operator (
::): Explicitly specify the namespace prefix.
int main() {
printf("%d\n", MyEngine::config_id);
return 0;
}
- The
usingDeclaration: Introduces a specific member into the current scope.
using MyEngine::computeSum;
int main() {
printf("%d\n", computeSum(5, 10));
return 0;
}
- The
using namespaceDirective: Introduces the entire namespace into the current scope.
using namespace MyEngine;
int main() {
printf("%d\n", config_id);
return 0;
}
- C++ Input and Output
C++ introduces the <iostream> library for handling input and output streams, replacing the need for format specifiers required by printf and scanf.
std::cout(Character Output) prints to the console using the<<operator.std::cin(Character Input) reads from the keyboard using the>>operator.std::endlflushes the buffer and inserts a newline.
#include <iostream>
int main() {
int age;
double salary;
std::cout << "Enter age and salary: ";
std::cin >> age >> salary;
std::cout << "Age: " << age << ", Salary: " << salary << std::endl;
return 0;
}
For everyday coding exercises, using namespace std; is convenient. However, in large-scale development, its highly recommended to use the std:: prefix or specific using declarations to avoid name clashes with the standard library.
- Default Parameters
4.1 Concept
Default parameters allow a function to be called without providing all arguments. If an argument is omitted, the compiler uses the predefined default value.
void configure(int port = 8080, int timeout = 30) {
std::cout << "Port: " << port << ", Timeout: " << timeout << std::endl;
}
int main() {
configure(); // Uses 8080 and 30
configure(3000); // Uses 3000 and 30
configure(3000, 60); // Uses 3000 and 60
return 0;
}
4.2 Rules
- Full Defaults: All parameters have default values.
- Partial Defaults: Defaults must be assigned from right to left without gaps.
- Declaration vs. Definition: Default values must be specified in the function declaration (usually in a header file), not in the definition. This is because the compiler needs the default values at the call site during compilation, before the linker connects the implementation.
- Function Overloading
C++ allows multiple functions with the same name but different parameter lists within the same scope. This is known as function overloading. Functions are differentiated by parameter types, counts, or the sequence of types.
int computeArea(int side) { return side * side; }
double computeArea(double radius) { return 3.14159 * radius * radius; }
void render(int id, char symbol) {}
void render(char symbol, int id) {}
5.1 Name Mangling
C++ achieves overloading through a process called name mangling. While C compilers generally use the raw function name internally, C++ compilers encode the function name with its parameter types to generate a unique internal symbol. For instance, under GCC, computeArea(int) might become _Z10computeAreai, while computeArea(double) becomes _Z10computeAread. This allows the linker to distinguish between overloaded functions. Return types are not included in the mangled name, which is why overloading based solely on return type is illegal.
- References
6.1 Concept
A reference creates an alias for an existing variable. It does not allocate new memory; instead, it shares the same memory address as the original entity.
int target = 50;
int& ref = target; // ref is an alias for target
6.2 Characteristics
- Must be initialized upon declaration.
- Once bound to an entity, it cannot be reassigned to refer to another entity.
- A variable can have multiple references.
6.3 Const References
Constant references prevent modification of the referenced value. They are essential for binding to temporary objects generated by implicit type conversions.
const int& safe_ref = 100; // Binds to a temporary int
double val = 9.8;
const int& int_ref = val; // Binds to temporary int created during conversion
6.4 Use Cases and Pitfalls
- Function Parameters: Passing by reference avoids copying large structures and allows in-place modification.
- Return Values: Returning by reference can be highly efficient, but never return a reference to a local variable, as its memory is destroyed when the function exits, resulting in undefined behavior.
int& dangerous_add(int x, int y) {
int local_sum = x + y;
return local_sum; // FATAL: Returns reference to destroyed stack memory
}
6.5 References vs. Pointers
- References must be initialized; pointers can be null.
- References cannot be reassigned; pointers can point to different addresses.
- Pointers require dereferencing (
*); references are used directly. sizeof(reference)yields the size of the object;sizeof(pointer)yields the size of an address (4 or 8 bytes).- Pointers can be multi-level (
**); references cannot.
- Inline Functions
An inline function suggests to the compiler that the function body should be inserted directly at the call site, eliminating the overhead of a function call (stack frame creation). This is a space-for-time trade-off.
7.1 Key Traits
- The
inlinekeyword is merely a suggestion; compilers may ignore it for recursive or very large functions. - Inline functions should typically be defined in header files. If declared in a header but defined in a separate source file, the linker will fail because the compiler cannot substitute the code without the definition being visible.
7.2 Alternatives to Macros
Macros lack type checking and are difficult to debug. C++ recommends:
constorenumfor defining constants.inlinefunctions for defining short, frequently executed logic.
- The
autoKeyword (C++11)
8.1 Type Deduction
In C++11, auto directs the compiler to deduce the variable's type from its initializer. This is invaluable when dealing with complex types like iterators.
std::map<std::string, int> records;
// Instead of: std::map<std::string, int>::iterator it = records.begin();
auto it = records.begin();
8.2 Usage Rules
- A variable declared with
automust be initialized. - When deducing pointers,
autoandauto*function similarly. To deduce a reference,auto&must be used explicitly. - When declaring multiple variables in one statement, the deduced types must match exactly.
8.3 Restrictions
autocannot be used for function parameters.autocannot be used to deduce the type of an array.
- Range-Based
forLoop (C++11)
Iterating over containers and arrays is simplified with range-based loops. The syntax is for (declaration : range).
int scores[] = {85, 90, 95};
for (auto& mark : scores) {
mark += 5; // Modify elements using reference
}
for (auto mark : scores) {
std::cout << mark << " ";
}
The iterable range must be well-defined. For instance, passing an array to a function causes it to decay into a pointer, losing its size information, which breaks the range-based loop.
- The
nullptrKeyword (C++11)
Historically, NULL was defined as 0 or ((void*)0). This caused ambiguity in function overloading when an integer zero was intended as a null pointer.
void process(int value);
void process(int* ptr);
int main() {
process(0); // Calls process(int)
process(NULL); // Ambiguous: often calls process(int) instead of process(int*)
return 0;
}
C++11 introduced nullptr as a dedicated null pointer constant of type std::nullptr_t. It implicitly converts to any pointer type but never to an integer type, resolving the overload resolution issues safely.
process(nullptr); // Unambiguously calls process(int*)