Skip to content

Instantly share code, notes, and snippets.

@stungeye
Last active February 13, 2023 18:23
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save stungeye/e8d5ea1f428f513edd1e159fc80b445d to your computer and use it in GitHub Desktop.
Has-As Class Relationship with and without Pointers
/*
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";
}
/*
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";
}
/*
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";
}
/*
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