C++ Best Practices
Overview
Following best practices helps write clean, efficient, and maintainable C++ code.
Code Style and Organization
Naming Conventions
cpp
// Use meaningful names
class StudentDatabase {
private:
std::vector<Student> students_;
int max_capacity_;
public:
void addStudent(const Student& student);
bool isFull() const;
};
// Use snake_case for variables and functions
int calculate_average(const std::vector<int>& numbers);
std::string user_name;
// Use PascalCase for classes and structs
class FileManager;
struct Point2D;
// Use UPPER_CASE for constants
const int MAX_BUFFER_SIZE = 1024;
const double PI = 3.14159;Memory Management
Prefer Smart Pointers
cpp
#include <memory>
#include <vector>
class ResourceHandler {
public:
// Good: Use smart pointers
std::unique_ptr<Resource> createResource() {
return std::make_unique<Resource>();
}
// Bad: Raw pointers with manual management
Resource* createResourceBad() {
Resource* ptr = new Resource();
// Easy to forget delete!
return ptr;
}
};
// Use RAII principle
class FileHandler {
private:
std::ofstream file_;
public:
FileHandler(const std::string& filename) : file_(filename) {
if (!file_.is_open()) {
throw std::runtime_error("Cannot open file");
}
}
~FileHandler() {
if (file_.is_open()) {
file_.close();
}
}
void write(const std::string& data) {
file_ << data;
}
};Error Handling
Use Exceptions Appropriately
cpp
#include <stdexcept>
class Calculator {
public:
double divide(double a, double b) {
if (b == 0.0) {
throw std::invalid_argument("Division by zero");
}
return a / b;
}
// Use noexcept for functions that don't throw
int add(int a, int b) noexcept {
return a + b;
}
};
int main() {
Calculator calc;
try {
double result = calc.divide(10.0, 0.0);
} catch (const std::invalid_argument& e) {
std::cout << "Error: " << e.what() << std::endl;
}
return 0;
}Performance Optimization
Use const and References
cpp
class DataProcessor {
public:
// Good: Pass by const reference
void processData(const std::vector<int>& data) const {
// Process data without copying
}
// Bad: Pass by value (creates copy)
void processDataBad(std::vector<int> data) {
// Unnecessary copy
}
// Return by value when appropriate (NRVO/move semantics)
std::vector<int> getProcessedData() const {
std::vector<int> result;
// Process and return
return result; // Move semantics will be used
}
};Modern C++ Features
Use Modern C++ Constructs
cpp
#include <memory>
#include <vector>
#include <algorithm>
// Use auto for type inference
void processContainer(const std::vector<int>& container) {
for (const auto& item : container) {
// Process item
}
}
// Use range-based for loops
void printData(const std::vector<std::string>& data) {
for (const auto& str : data) {
std::cout << str << std::endl;
}
}
// Use lambda expressions
void filterData(std::vector<int>& data) {
data.erase(
std::remove_if(data.begin(), data.end(),
[](int x) { return x < 0; }),
data.end()
);
}
// Use constexpr for compile-time constants
constexpr int BUFFER_SIZE = 1024;
constexpr double PI = 3.14159265359;Thread Safety
Proper Synchronization
cpp
#include <mutex>
#include <atomic>
class ThreadSafeCounter {
private:
std::mutex mutex_;
std::atomic<int> atomic_counter_{0};
int counter_ = 0;
public:
// Using mutex
void increment() {
std::lock_guard<std::mutex> lock(mutex_);
++counter_;
}
int getValue() const {
std::lock_guard<std::mutex> lock(mutex_);
return counter_;
}
// Using atomic for simple operations
void atomicIncrement() {
++atomic_counter_;
}
int getAtomicValue() const {
return atomic_counter_.load();
}
};Design Patterns
Follow SOLID Principles
cpp
// Single Responsibility Principle
class Logger {
public:
void log(const std::string& message);
};
class DataProcessor {
public:
void process(const Data& data);
};
// Open/Closed Principle
class Shape {
public:
virtual double area() const = 0;
virtual void draw() const = 0;
};
class Circle : public Shape {
public:
double area() const override;
void draw() const override;
};
// Dependency Inversion Principle
class INotificationService {
public:
virtual void send(const std::string& message) = 0;
};
class EmailService : public INotificationService {
public:
void send(const std::string& message) override;
};
class UserManager {
private:
std::unique_ptr<INotificationService> notifier_;
public:
UserManager(std::unique_ptr<INotificationService> notifier)
: notifier_(std::move(notifier)) {}
};