Refactoring a codebase from manual memory management to RAII

Edit: @Ben Cottrell's comment said this was similar to a question about spaghetti code. While both questions involve large codebases, mine addresses a specific technical pattern: manual memory management vs RAII, with concrete examples of the current structure. The other question asks for general legacy code strategies, and its answers (VCS, testing) don't address my specific object lifecycle refactoring challenge. I've inherited a large codebase (around 200k lines) and I'm trying to understand the best way to modernize its object lifecycle management. The code is actually well-organized in many ways: The project is split into many classes (100+) with good separation of concerns. However, it has an unusual pattern for object lifecycle management that relies heavily on manual memory management with raw pointers. The current pattern looks like this: Each class T follows a specific structure: Headers (T.h) contain class declarations with basic constructors/destructors and accessors Implementation files (T.cpp) have mostly empty constructors/destructors Separate files (T_func.h/cpp) contain the actual initialization logic in standalone functions The initialization happens through functions like T_func::build_T(T&, ...) rather than in constructors. Similarly, cleanup occurs through T_func::clear(T*) functions instead of destructors. There are some interesting constraints: Some destructors are protected, forcing you to use the clear(t) functions (sometimes those just delete t, triggering the empty constructor, so this is equivalent to delete t but prevents stack allocation) Initialization can be multi-stage, where build_T_1() must be called before build_T_2() These initialization stages often work with hierarchical object structures The stages can span multiple modules The getters and setters are basically just direct access to member variables. I'd like to refactor this to use modern C++ patterns - specifically RAII where objects manage their own lifecycle and can be stack-allocated. Any suggestions on the best approach?

Feb 3, 2025 - 20:31
 0
Refactoring a codebase from manual memory management to RAII

Edit: @Ben Cottrell's comment said this was similar to a question about spaghetti code. While both questions involve large codebases, mine addresses a specific technical pattern: manual memory management vs RAII, with concrete examples of the current structure. The other question asks for general legacy code strategies, and its answers (VCS, testing) don't address my specific object lifecycle refactoring challenge.

I've inherited a large codebase (around 200k lines) and I'm trying to understand the best way to modernize its object lifecycle management. The code is actually well-organized in many ways:

The project is split into many classes (100+) with good separation of concerns. However, it has an unusual pattern for object lifecycle management that relies heavily on manual memory management with raw pointers.

The current pattern looks like this:

Each class T follows a specific structure:

  • Headers (T.h) contain class declarations with basic constructors/destructors and accessors
  • Implementation files (T.cpp) have mostly empty constructors/destructors
  • Separate files (T_func.h/cpp) contain the actual initialization logic in standalone functions

The initialization happens through functions like T_func::build_T(T&, ...) rather than in constructors. Similarly, cleanup occurs through T_func::clear(T*) functions instead of destructors.

There are some interesting constraints:

  • Some destructors are protected, forcing you to use the clear(t) functions (sometimes those just delete t, triggering the empty constructor, so this is equivalent to delete t but prevents stack allocation)
  • Initialization can be multi-stage, where build_T_1() must be called before build_T_2()
  • These initialization stages often work with hierarchical object structures
  • The stages can span multiple modules

The getters and setters are basically just direct access to member variables.

I'd like to refactor this to use modern C++ patterns - specifically RAII where objects manage their own lifecycle and can be stack-allocated. Any suggestions on the best approach?