Skip to content

Commit bbced5c

Browse files
committed
tones of updates
1 parent 3b3e5cf commit bbced5c

19 files changed

+582
-80
lines changed

CMakeLists.txt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,6 @@ add_executable(default_0_delete_meaning src/class/default_0_delete_meaning.cpp)
123123

124124
add_executable(object_slicing src/class/object_slicing.cpp)
125125

126-
add_executable(circular_dependency
127-
src/class/Circular_Dependency/circular_dependency.cpp
128-
src/class/Circular_Dependency/a.cpp
129-
src/class/Circular_Dependency/b.cpp
130-
src/class/Circular_Dependency/c.cpp)
131-
132126
add_executable(static_member_function src/class/static_member_function.cpp)
133127

134128
add_executable(constructor_initialization_list src/class/constructor_initialization_list.cpp)
@@ -335,7 +329,12 @@ add_executable(std_invoke src/std_invoke.cpp)
335329
add_executable(structured_binding_declaration src/structured_binding_declaration.cpp)
336330

337331
add_executable(asynchronous_programming src/asynchronous_programming.cpp)
332+
target_link_libraries(asynchronous_programming -pthread)
333+
334+
add_executable(circular_dependency src/class/circular_dependency/circular_dependency.cpp src/class/circular_dependency/classA.cpp src/class/circular_dependency/classB.cpp)
335+
338336

337+
add_executable(core_dump src/core_dump.cpp)
339338

340339

341340
if(${CMAKE_GNU_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} GREATER_EQUAL 13)

CMakePresets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"displayName": "Ninja Multi-Config",
1212
"description": "Use Ninja with multiple configurations",
1313
"generator": "Ninja Multi-Config",
14-
"binaryDir": "${sourceDir}/build/${presetName}",
14+
"binaryDir": "${sourceDir}/build/",
1515
"cacheVariables": {
1616
"CMAKE_POLICY_DEFAULT_CMP0048": "NEW",
1717
"CMAKE_CONFIGURATION_TYPES": "Debug;Release;RelWithDebInfo;MinSizeRel"

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ read more [here](https://ros-developer.com/2017/11/08/docker/)
183183
* [Class Constructor Initializationlist](src/class/constructor_initialization_list.cpp)
184184
* [Class Constructor Aggregate/ Copy/ Default/ Direct/ Value/ Uniform/ Zero Initialization, Initializer List](docs/aggregate-copy-default-direct-value-zero.md)
185185
* [Copy/ Move Constructor, rvalue, lvalue, move semantic](docs/copy_constructor_move_constructor_rvalue_lvalue_move_semantic.md)
186-
* [Cyclic (Circular) Dependency](src/class/Circular_Dependency)
186+
* [Cyclic (Circular) Dependency](docs/circular_dependencies.md)
187187
* [Default(=default), Deleted (=delete) Constructors](docs/default_constructors_=default_0_delete.md)
188188
* [Diamond Problem Virtual Inheritance](src/class/diamond_problem_virtual_inheritance.cpp)
189189
* [Explicit Constructor, Converting Constructor](docs/explicit_constructor.md)
@@ -225,11 +225,12 @@ read more [here](https://ros-developer.com/2017/11/08/docker/)
225225

226226

227227

228-
## [Advance C++ Concepts/ Idiom](#)
228+
## [Advance C++ Concepts and Idioms](#)
229229

230230
* [Argument-dependent lookup](src/argument_dependent_lookup.cpp)
231231
* [Buffer overflow](src/buffer_overflow.cpp)
232232
* [Copy and Swap](docs/copy-and-swap_idiom.md)
233+
* [Creating and Debugging Dump File](docs/creating_and_debugging_dump_file.md)
233234
* [Static Casting, Dynamic Casting](src/cast.cpp)
234235
* [Run-Time Type Information(RTTI)](src/RTTI.cpp)
235236
* [Curiously Recurring Template Pattern (CRTP)](src/CRTP.cpp)

docs/circular_dependencies.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Circular Dependency
2+
Circular dependencies in C++ occur when two or more classes or files depend on each other directly or indirectly, creating a loop in the dependency graph. This can lead to issues like incomplete type errors, linking errors, or even runtime errors. To resolve circular dependencies, you can consider the following strategies:
3+
4+
1. **Forward Declarations**: Use forward declarations to declare a class without defining it. This can break the dependency cycle by letting you refer to a class in another class without needing its full definition.
5+
6+
2. **Refactoring the Code**: Sometimes, circular dependencies indicate a design issue. Consider refactoring your classes or code structure. This could involve splitting a class into multiple classes, combining classes, or moving part of the functionality to a new class.
7+
8+
3. **Using Interfaces or Abstract Classes**: Define interfaces or abstract classes that classes depend on, rather than depending on concrete implementations. This can reduce coupling between classes.
9+
10+
4. **Dependency Inversion**: Implement dependency inversion, a principle where high-level modules should not depend on low-level modules, but both should depend on abstractions.
11+
12+
5. **Using Pointers or References**: In some cases, using pointers or references instead of value objects can help, especially when combined with forward declarations.
13+
14+
6. **Header File Management**: Organize your header files and use include guards (`#ifndef`, `#define`, `#endif`) to prevent multiple inclusions.
15+
16+
## 1. Forward Declarations
17+
18+
Here's a basic example of using forward declarations to resolve a circular dependency:
19+
20+
Let's create a full example with `ClassA` and `ClassB`. This will illustrate how to resolve circular dependencies using forward declarations and pointers.
21+
22+
### classA.hpp
23+
```cpp
24+
#ifndef CLASSA_H
25+
#define CLASSA_H
26+
27+
// Forward declaration of ClassB
28+
class ClassB;
29+
30+
class ClassA {
31+
public:
32+
ClassA();
33+
void setClassB(ClassB *b);
34+
void doSomethingWithB();
35+
void exampleMethod();
36+
37+
private:
38+
ClassB *b;
39+
};
40+
41+
#endif // CLASSA_H
42+
```
43+
44+
### classA.cpp
45+
```cpp
46+
#include "classA.hpp"
47+
#include "classB.hpp"
48+
#include <iostream>
49+
50+
ClassA::ClassA() : b(nullptr) {}
51+
52+
void ClassA::setClassB(ClassB *b) { this->b = b; }
53+
54+
void ClassA::doSomethingWithB() {
55+
if (b) {
56+
// Assuming ClassB has a method exampleMethod() that we want to call
57+
std::cout << "ClassA is calling method from classB." << std::endl;
58+
b->exampleMethod();
59+
} else {
60+
std::cout << "ClassB instance is not set in ClassA." << std::endl;
61+
}
62+
}
63+
64+
void ClassA::exampleMethod() {
65+
std::cout << "Method in ClassA called." << std::endl;
66+
}
67+
```
68+
69+
### classB.hpp
70+
```cpp
71+
#ifndef CLASSB_H
72+
#define CLASSB_H
73+
74+
#include "classA.hpp"
75+
76+
class ClassB {
77+
public:
78+
ClassB();
79+
void setClassA(ClassA *a);
80+
void exampleMethod();
81+
void doSomethingWithA();
82+
83+
private:
84+
ClassA *a;
85+
};
86+
87+
#endif // CLASSB_H
88+
```
89+
90+
### classB.cpp
91+
```cpp
92+
#include "classB.hpp"
93+
#include <iostream>
94+
95+
ClassB::ClassB() : a(nullptr) {}
96+
97+
void ClassB::exampleMethod() {
98+
std::cout << "Method in ClassB called." << std::endl;
99+
}
100+
101+
void ClassB::doSomethingWithA() {
102+
std::cout << "ClassB is calling method from classA." << std::endl;
103+
a->exampleMethod();
104+
}
105+
void ClassB::setClassA(ClassA *a) { this->a = a; }
106+
```
107+
108+
### Explanation:
109+
- **ClassA.hpp**: Forward declares `ClassB` and defines `ClassA`. It includes a method to set a pointer to a `ClassB` object and a method to interact with `ClassB`.
110+
- **ClassA.cpp**: Includes both `ClassA.hpp` and `ClassB.hpp`. It defines the methods of `ClassA`.
111+
- **ClassB.hpp**: Includes `ClassA.hpp` and defines `ClassB`. It could also include a pointer to a `ClassA` object if needed.
112+
- **ClassB.cpp**: Implements the methods of `ClassB`.
113+
114+
### Usage:
115+
To use these classes, create instances of `ClassA` and `ClassB` in your main program, and set the `ClassB` instance in `ClassA` using `setClassB`.
116+
117+
### Note:
118+
In this setup, `ClassB` doesn't hold a reference to `ClassA`. If you need bidirectional communication, you would add a pointer to `ClassA` in `ClassB` and set it similarly. Be mindful of object ownership and lifetimes to avoid memory leaks or dangling pointers.
119+
120+
121+
122+
## 2. Dependency Inversion
123+
124+
125+
To implement Dependency Inversion in your current setup with `ClassA` and `ClassB`, you'll need to introduce an interface or an abstract class. Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules but should depend on abstractions. Abstractions, in this context, should not depend on details; instead, details should depend on abstractions.
126+
127+
Here's how you can refactor your code:
128+
129+
1. **Create Abstract Classes or Interfaces**: Define abstract classes or interfaces that `ClassA` and `ClassB` will implement. These abstract classes or interfaces will declare the methods that need to be overridden.
130+
131+
2. **Change ClassA and ClassB to Depend on Abstractions**: Instead of having `ClassA` and `ClassB` directly depend on each other, make them depend on these new abstractions.
132+
133+
### Example Refactoring:
134+
135+
1. **Define Abstract Classes/Interfaces**
136+
137+
**IA.h**
138+
```cpp
139+
#ifndef IA_H
140+
#define IA_H
141+
142+
class IA {
143+
public:
144+
virtual void exampleMethod() = 0;
145+
virtual void doSomethingWithB() = 0;
146+
virtual ~IA() {}
147+
};
148+
149+
#endif // IA_H
150+
```
151+
152+
**IB.h**
153+
```cpp
154+
#ifndef IB_H
155+
#define IB_H
156+
157+
class IB {
158+
public:
159+
virtual void exampleMethod() = 0;
160+
virtual void doSomethingWithA() = 0;
161+
virtual ~IB() {}
162+
};
163+
164+
#endif // IB_H
165+
```
166+
167+
2. **Modify ClassA and ClassB to Implement These Interfaces**
168+
169+
**ClassA.h**
170+
```cpp
171+
#ifndef CLASSA_H
172+
#define CLASSA_H
173+
174+
#include "IA.h"
175+
#include "IB.h"
176+
177+
class ClassB; // Forward declaration
178+
179+
class ClassA : public IA {
180+
public:
181+
ClassA();
182+
void setClassB(IB* b);
183+
void doSomethingWithB() override;
184+
void exampleMethod() override;
185+
186+
private:
187+
IB* b;
188+
};
189+
190+
#endif // CLASSA_H
191+
```
192+
193+
**ClassB.h**
194+
```cpp
195+
#ifndef CLASSB_H
196+
#define CLASSB_H
197+
198+
#include "IB.h"
199+
#include "IA.h"
200+
201+
class ClassA; // Forward declaration
202+
203+
class ClassB : public IB {
204+
public:
205+
ClassB();
206+
void setClassA(IA* a);
207+
void exampleMethod() override;
208+
void doSomethingWithA() override;
209+
210+
private:
211+
IA* a;
212+
};
213+
214+
#endif // CLASSB_H
215+
```
216+
217+
3. **Implement the Classes**
218+
219+
In your `.cpp` files for `ClassA` and `ClassB`, you will implement these methods as before, but now they depend on the interfaces (`IA` and `IB`) instead of the concrete classes.
220+
221+
### Advantages of This Approach:
222+
223+
- **Reduced Coupling**: Classes depend on abstractions, not concrete implementations, reducing coupling.
224+
- **Flexibility**: Makes it easier to substitute different implementations of `IA` or `IB` without changing `ClassA` or `ClassB`.
225+
- **Testability**: Easier to mock or stub out dependencies for unit testing.
226+
227+
By following this approach, you adhere to the Dependency Inversion Principle, one of the SOLID principles of object-oriented design, which leads to more maintainable and flexible code.
228+
229+
230+
231+
232+
233+
234+

0 commit comments

Comments
 (0)