Last active
February 13, 2023 18:23
Star
You must be signed in to star a gist
Has-As Class Relationship with and without Pointers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Imagine this code was written before initializer lists. (Pre C++11) | |
When compared with `main_without_pointer_oldskool_and_broken.cpp`: | |
- Point's location member is a pointer to a Location. | |
- Location does not need a default constructor. | |
- The location member is only constructed once. | |
*/ | |
#include <iostream> | |
#include <cmath> | |
class Location { | |
int x, y; | |
public: | |
Location(int _x, int _y) { | |
std::cout << "Location Constructor" << "\n"; | |
x = _x; | |
y = _y; | |
} | |
double distanceToOrigin() { | |
return std::sqrt(x * x + y * y); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Location& location) { | |
out << "X: " << location.x << " Y: " << location.y; | |
return out; | |
} | |
}; | |
class Shape { | |
Location* location; // Shape has-a pointer to a Point. | |
public: | |
Shape(int x, int y) { | |
std::cout << "Shape Constructor" << "\n"; | |
location = new Location(x, y); // Allocate the Point from the heap. | |
} | |
~Shape() { | |
std::cout << "Shape Destructor" << "\n"; | |
delete location; // Release the Point back to the heap. | |
location = nullptr; | |
} | |
double distanceToOrigin() { | |
return location->distanceToOrigin(); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Shape& shape) { | |
out << "Shape! " << *shape.location; | |
return out; | |
} | |
}; | |
int main() { | |
Shape myShape(2, 4); // Stack | |
std::cout << myShape << "\n"; | |
std::cout << myShape.distanceToOrigin() << "\n"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
In this version of the code we are using a smart pointer, specifically std::shared_ptr to | |
avoid the use of a raw pointer, and to avoid the delete to manually new/delete the memory | |
used by the pointer. | |
*/ | |
#include <iostream> | |
#include <cmath> | |
class Location { | |
int x, y; | |
public: | |
Location(int _x, int _y) : x{ _x }, y{ _y } { | |
std::cout << "Location Constructor" << "\n"; | |
} | |
double distanceToOrigin() { | |
return std::sqrt(x * x + y * y); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Location& location) { | |
out << "X: " << location.x << " Y: " << location.y; | |
return out; | |
} | |
}; | |
class Shape { | |
std::unique_ptr<Location> location; // Shape has-a smart pointer to a Point. | |
public: | |
Shape(int x, int y) : location{std::make_unique<Location>(x, y)} { | |
std::cout << "Shape Constructor" << "\n"; | |
} | |
~Shape() { | |
std::cout << "Shape Destructor" << "\n"; | |
// No need to manually delete the pointer memory here! | |
// RIAA within the unique_ptr handles that for us. | |
} | |
double distanceToOrigin() { | |
return location->distanceToOrigin(); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Shape& shape) { | |
out << "Shape! " << *shape.location; | |
return out; | |
} | |
}; | |
int main() { | |
Shape myShape(2, 4); // Stack | |
std::cout << myShape << "\n"; | |
std::cout << myShape.distanceToOrigin() << "\n"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Imagine this code was written once initializer lists exist. (Post C++11) | |
We no longer need a pointer for Shape's Location member as it can be properly | |
constructed using an initializer list. | |
When compared with `main_with_pointer.cpp` & `main_without_pointer_oldskool_and_broken.cpp`: | |
- Point's location member is a Location object, not a pointer. | |
- Location does not need a default constructor. | |
- The location member is only constructed once. | |
*/ | |
#include <iostream> | |
#include <cmath> | |
class Location { | |
int x, y; | |
public: | |
Location(int x, int y) : x{x}, y{y} { | |
std::cout << "Location Constructor" << "\n"; | |
} | |
double distanceToOrigin() { | |
return std::sqrt(x * x + y * y); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Location& location) { | |
out << "X: " << location.x << " Y: " << location.y; | |
return out; | |
} | |
}; | |
class Shape { | |
// Shape has-a Location | |
// No pointer required because we can list initialize it along with the constructor. | |
Location location; | |
public: | |
Shape(int x, int y) : location{x, y} { // Note the list initialization of position. | |
std::cout << "Shape Constructor" << "\n"; | |
} | |
double distanceToOrigin() { | |
return location.distanceToOrigin(); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Shape& shape) { | |
out << "Shape! " << shape.location; | |
return out; | |
} | |
}; | |
int main() { | |
Shape myShape(2, 4); // Stack | |
std::cout << myShape << "\n"; | |
std::cout << myShape.distanceToOrigin() << "\n"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Imagine this code was written before initializer lists. (Pre C++11) | |
We might be inclined to want to turn Shape's location member into a | |
pointer to avoid the location being constructed immediately when it | |
is defined and then overwritten during Shape's constructor. | |
When compared with `main_with_pointer.cpp`: | |
- Point's location member is a Location object, not a pointer. | |
- Location needs a default constructor. | |
- The location member is only constructed twice. | |
*/ | |
#include <iostream> | |
#include <cmath> | |
class Location { | |
int x, y; | |
public: | |
// The code will not compile without this constructor. As the location | |
// member on line 44 will be constructed with this no argument constructor. | |
Location() { | |
std::cout << "Default Constructor" << "\n"; | |
} | |
Location(int _x, int _y) { | |
std::cout << "Location Constructor" << "\n"; | |
x = _x; | |
y = _y; | |
} | |
double distanceToOrigin() { | |
return std::sqrt(x * x + y * y); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Location& location) { | |
out << "X: " << location.x << " Y: " << location.y; | |
return out; | |
} | |
}; | |
class Shape { | |
// Shape has-a Location | |
// Because we aren't using an initializer list, this Location gets | |
// constructed immediately using Location's default constructor. | |
Location location; | |
public: | |
Shape(int x, int y) { | |
std::cout << "Shape Constructor" << "\n"; | |
// location was already constructed once above and gets over written here. | |
location = Location(x, y); | |
} | |
double distanceToOrigin() { | |
return location.distanceToOrigin(); | |
} | |
friend std::ostream& operator<<(std::ostream& out, const Shape& shape) { | |
out << "Shape! " << shape.location; | |
return out; | |
} | |
}; | |
int main() { | |
Shape myShape(2, 4); // Stack | |
std::cout << myShape << "\n"; | |
std::cout << myShape.distanceToOrigin() << "\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment