KAHIBARO
Discord Login Register

Inheritance

Base classes

Inheritance in C++ lets you define a new class that reuses and extends an existing one. The existing class is called a base class. The new class is called a derived class. Geant4 uses inheritance heavily, so understanding the role of base classes is essential before you can read or write Geant4 code comfortably.

A base class in C++ usually represents a general concept, such as a generic detector, a generic shape, or a generic user action. It provides data members and member functions that are common to all its derived classes. In Geant4, most base classes are abstract interfaces. An abstract class has at least one pure virtual function. A pure virtual function is declared like this:
$$\texttt{virtual void DoSomething() = 0;}$$
Such a function has no implementation in the base class. It must be implemented in any concrete derived class.

A pure virtual function is declared with = 0 and must be overridden in any concrete derived class, otherwise the derived class is also abstract and cannot be instantiated.

Geant4 base classes typically define what functions a user must override to plug in their own behavior. For example, a generic detector construction base class defines a Construct() function but does not know your geometry. A generic physics list base class defines how to construct particles and physics processes but does not know which ones you need. You must create a derived class and implement the required functions.

Base classes can also provide default behavior that derived classes can reuse. In such cases, the base class contains real implementations of some functions. Derived classes may override only the ones that need customization.

In Geant4 interfaces you will often see pointers and references to base classes, like G4VUserDetectorConstruction or G4VModularPhysicsList. This is called polymorphism. It lets Geant4 call your derived class methods through a base class pointer, without needing to know the exact derived type.

Derived classes

A derived class inherits all the accessible data members and member functions from its base class. It can then add new members or override virtual functions to change or specialize the behavior.

In C++ you define a derived class like this:

cpp
class MyDetectorConstruction : public G4VUserDetectorConstruction {
  // class body
};

Here G4VUserDetectorConstruction is the base class, and MyDetectorConstruction is a derived class. The keyword public specifies public inheritance, which is almost always what you want for Geant4 user classes.

If the base class declares a virtual function, the derived class can override it. In modern C++, you should use the override keyword to indicate that you intend to override a base class virtual function:

cpp
class MyDetectorConstruction : public G4VUserDetectorConstruction {
public:
  MyDetectorConstruction();
  ~MyDetectorConstruction() override;
  G4VPhysicalVolume* Construct() override;
};

When you override a Geant4 virtual function, always match the function signature exactly and use the override keyword. A mismatched signature silently creates a new function instead of overriding, and Geant4 will never call it.

A derived class can also call methods of its base class using the BaseClassName::MethodName() syntax if it needs to extend rather than replace an implementation. Many Geant4 base classes are purely abstract, so there is no base implementation to call, but some advanced classes do provide partial implementations.

Geant4 code and the Geant4 kernel usually interact with your objects through base class pointers. For example, you give the run manager a pointer to your derived detector construction object, but the run manager keeps it as a pointer to the base class:

cpp
auto detector = new MyDetectorConstruction();
runManager->SetUserInitialization(detector);  // takes G4VUserDetectorConstruction*

At runtime, when the kernel needs to build the geometry, it calls Construct() on the base class pointer. Because Construct() is virtual and overridden in your derived class, your implementation is executed. This is the typical polymorphic pattern that runs throughout Geant4.

Geant4 user classes

Geant4 is designed around a set of abstract base classes that define the structure of a simulation. Your role as a user is to implement concrete derived classes that specialize this behavior for your specific problem. These are usually called user classes.

Examples of important Geant4 base classes that you must derive from include:

G4VUserDetectorConstruction, where you describe your geometry and materials. You derive a class, implement Construct(), and return the world volume.

G4VUserPhysicsList or, more commonly in modern Geant4, you use predefined physics lists derived from G4VModularPhysicsList. If you write your own physics list, you override functions such as ConstructParticle() and ConstructProcess().

G4VUserPrimaryGeneratorAction, where you define how primary particles are generated at the start of each event. Your derived class implements the GeneratePrimaries(G4Event* event) method.

G4UserRunAction, G4UserEventAction, G4UserSteppingAction, G4UserTrackingAction, and G4UserStackingAction, which allow you to inject your own behavior at different stages of the simulation. You implement the relevant virtual functions in derived classes, such as BeginOfRunAction, EndOfEventAction, or UserSteppingAction.

G4VSensitiveDetector, which represents a detector element that can record hits. Your derived class overrides ProcessHits to store information when particles interact in that detector.

The run manager expects to receive pointers to these base classes through methods like SetUserInitialization and SetUserAction. In your main program, you typically write:

cpp
runManager->SetUserInitialization(new MyDetectorConstruction());
runManager->SetUserInitialization(new MyPhysicsList());
runManager->SetUserAction(new MyPrimaryGenerator());
runManager->SetUserAction(new MyRunAction());

Here each new MyClass() is a derived class instance. The run manager only knows about the corresponding base class interfaces, so it can remain general and reusable while your code provides the specific behavior.

Every required Geant4 user class must be implemented by inheriting from the correct base class and overriding the required virtual methods. If you forget to register a user class with the run manager, or if you implement the wrong signature, the corresponding part of the simulation will not run as intended.

This inheritance based design keeps your code modular. Geometry, physics, primary generation, and user actions are separated into different derived classes. As a beginner, you do not need to design your own inheritance hierarchies inside your application. You only need to derive from the Geant4 base classes and implement what they ask for. Over time, as your simulations become more complex, you may create your own base classes and derived classes to share code between different detectors or setups, using the same principles that Geant4 itself uses.

Views: 8

Comments

Please login to add a comment.

Don't have an account? Register now!