Here's a code sample I wrote with encapsulation and composition in mind:
class Bullet {
private:
Vector2 position;
Vector2 speed;
public:
void move(float time_delta) {
position += speed * time_delta;
}
};
Basically, there's just a projectile moving in nowhere. However, a bullet can actually e. g. ricochet off a wall, having its speed changed significantly. Is there a good way of considering such interactions? I neither want my Bullet to know about "higher-rank" classes (which are supposed to use it themselves) nor write a single-use solution like this one:
template<typename F> void move(float time_delta, F collision_checker);
UPDATE: worth reading if you want this question narrowed. Here's a simplified example of the wished logic for moving Bullets (I don't exactly mean the Bullet::move() member function!) and their interactions with other entities:
Vector2 destination = bullet.position + bullet.speed * time_delta;
if (std::optional<Creature> target = get_first_creature(bullet.position, destination)) {
// decrease Bullet::speed depending on the target (and calculate the damage)
} else if (std::optional<Wall> obstacle = get_first_wall(bullet.position, destination)) {
// calculate the ricochet changing Bullet::position and Bullet::speed
}
All pieces of code represented by comments are supposed to use some properties of the Creature and Wall classes.
From a design point of view, it is probably best if your bullet doesn't know how to detect when it's ... passing_through an obstacle (scnr). So it might be better to turn your Bullet class in to a struct, i.e. have it behave like a thing that is acted upon instead of a thing that acts.
You can still add your convenience function but have it be non-mutating:
struct Bullet {
Vector2 position;
Vector2 speed;
Vector2 move(float time_delta) const {
return position + speed * time_delta;
}
};
This way you can compute the collisions from the calling scope:
auto dest = bullet.move(dt);
while (std::optional<Collision> const col = detectCollision(bullet.position,dest)) {
bullet.position = col->intersectPoint;
bullet.speed = col->reflectedSpeed;
dest = col->reflectDest;
}
bullet.position = dest;
Here detectCollision checks whether the line from the bullet's current position to the new position dest intersects with any obstacle and computes the parameters of the reflection. Effectively you zig-zag your way to the destination that will result from all successive ping-pongs of the bullet with potential obstacles.
Related
It's easier for me to explain this kind of stuff in videogame terms. I'll try to be as clear as possible please bear with me...
I have a Bullet class that's made of different components, such as a Sprite component and a Transform Component... It would look something like this:
class Bullet
{
public:
Bullet( texture2D texture, Rectangle sourceRectangle, Rectangle destinationRectangle )
Bullet();
~Bullet();
// Returns a pointer to its sprite component so that I can use it wherever I want in the scene:
Sprite* const Sprite() const{ return this->sprite; }
// Setters:
void SetAngle( float new_angle ){ this->angle = new_angle; }
void SetSpeed( float new_speed ){ this->speed = new_speed; }
void SetRadius( float new_radius ){ this->radius = new_radius; }
private:
// Sprite component:
Sprite* sprite;
private:
// Regular member data:
float angle;
float speed;
float radius;
};
The sprite component is simply another class that holds a texture, source rectangle, destination rectangle, and some member functions to manipulate it (Move, Rotate, SetTexture, etc ). Nothing really special. It gets initialized within the constructor
What I need to do is to be able to copy one pointer to a bullet object entirely to another new pointer to a bullet, including the sprite component. Something like this:
// These arguments will be "fed" into the sprite component
Bullet* bullet1 = new Bullet( texture, sourceRect, destRect );
Bullet* bullet2 = new Bullet(); // Using default constructor.
*bullet2 = *bullet1;
Technically, doing it this way works. However, only the regular member data gets copied I think, but not the sprite component. It only points to the first bullet's sprite component. At least that's what I think is happening.
Hopefully I was clear explaining this issue, Thanks for taking the time.
Let say that I have a big class Circle with a lot of members and functions. To proceed a large amount of data I decided to create class PotentialCirlce (with only 3 members - x, y, r), do most of preprocessing based on PotentialCirlce and in the last stage create objects Circle.
a) is it correct approach? do It influence on performance or rather should I use only Circle.
It seems to me that I can use inheritance:
class potentialCircle {
protected:
point_t center;
unsigned int radius;
public:
potentialCircle(int a, int b, unsigned int r) : center{ point_t(a,b) }, radius{ r } {}
potentialCircle() = delete;
potentialCircle(const potentialCircle&) = default;
potentialCircle(potentialCircle&&) = default;
potentialCircle& operator=(const potentialCircle&) = default;
potentialCircle& operator=(potentialCircle&&) = default;
virtual ~potentialCircle() = default;
};
class Circle : public potentialCircle {
// members detected based on Hough Circle Transform
//point_t center; // coordinates of center point
point_t alternative_center; // needed when center is out of frame
//unsigned int radius; // radius
// members calculated based on Flood Fill algorithm (more realistic)
unsigned int area = 0;
float diameter = 0;
float perimeter = 0;
....
};
b) where should I put method which needs to compare two difference objects? one object of type Circle and one of PotentialCirle?
currently, I have defined below function as part of Circle
bool Circle::is_greater(const std::pair<potentialCircle, int>& point_pair) const;
but I don't have access to protected data members of potentialCircle, although Circle is inheriting from potentialCircle.
Maybe I should defined is_greater() as part of namepsace and make it a friend to Circle and potentialCircle.
Do you have better idea?
There are not really a good approach to compare objects of different types as it make little sense in practice. What would be the purpose of such comparisons.
Now even if you have a single class, if the ordering is not intransic to the type, it would be better to use an external class for sorting.
class CircleDiameterLess
{
public:
bool operator()(const Circle &lhs, const Circle &rhs)
{
return lhs.diameter < rhs.diameter;
}
};
That way, you can have multiple ways to sort data and it play nice with STL.
Another problem with your code if that it make little sense to have a class circle with a diameter that derives from a class potentialCircle with a radius. Your code will be hard to maintain because it is hard to understand.
You want to store either the diameter or the radius and compute the other one.
unsigned int get_diameter() const { return radius * 2; }
Member like alternative_center make no sense. A circle has only one center. If your class does not respect basic expectations, it will make the code hard to maintain as nobody would known that a circle has 2 centers including you in 3 months!
In a case like yours, it make make sense to add public accessors.
class potentialCircle
{
public:
unsigned int get_radius() const { return radius; }
....
};
That way, you can still make data private (or sometime protected) while having read only access to it. That way, you can write you comparison function as you wish. And in practice, if you have a class that represent a circle, you usually want at least being able to get basic properties like radius, aread, bounding rectangle by the way of a function.
Another thing is that public derivation as your (from potentialCircle) would only make senses if you have other classes that derives from it. However, if this is the case, then how would you compare the other kind of circles?
Notes:
With C++ 20, three way comparison would be even better.
I have an abstract base class called Shape, which looks something like this:
class Shape {
public:
Shape(Point center);
virtual bool overlaps(Shape *other) = 0;
private:
Point m_center; // has getter&setter
};
I'm having problems with the overlaps(Shape *other); method; I have no idea how to implement it in subclasses.
Let's take two examples, (I will probably have no more than two or three shapes) Circle and Rect.
Basically what I've tried is to create a two overloads in both classes after using forward declaration to allow Circle and Rect to "know" each other:
virtual bool Rect::overlaps(Circle *other);
virtual bool Rect::overlaps(Rect *other);
virtual bool Circle::overlaps(Circle *other);
virtual bool Circle::overlaps(Rect *other) { return other->overlaps(this); }
It's now easy to implement the maths inside all the overloads; however, I will get an error cannot allocate an object of abstract type 'Circle' and note: virtual bool Unit::overlaps(Unit *).
This is because my Circle and Rect classes only have methods with Circle * and Rect * as their parameters, but none with Unit *.
I also tried forward declarating Circle and Rect in my shape.h, but since forward declarations aren't the same classes as my actual Circle and Rect, I will only get the same error.
Without removing the common base class, is there a way to implement such behavior?
Or is there a workaround to make it work?
Additional Information
I have a 2D World class which contains vector<Shape *> m_shapes; and I will need to see if two shapes overlap each other;
for (unsigned int i = 0; i < m_shapes.size(); i++) {
if (certainShape->overlaps(m_shapes[i])) {
collapse();
}
}
Welcome to multiple dispatch! Essentially, you are asking for a method that is virtual with respect to the runtime type of more than one object - in your case, the types of two shapes being tested for overlap.
There are several common ways of implementing double dispatch in C++: for example, you could use the visitor pattern, or make a map based on RTTI. Selecting one or the other is up to you.
If you decide to go with the visitor pattern, you make the Shape "visitable" by adding the visit method.
Here is an example of the visitor-based approach. It is admittedly rather verbose, but it also addresses a complex task, so it is fair for it to require lots of code. I stripped the example below to the bare minimum - only two shapes with no data members, and methods that do not do anything except printing. This should be sufficient to get you started, though:
#include <iostream>
using namespace std;
class ShapeVisitor;
struct Shape {
virtual void accept(ShapeVisitor& v) = 0;
virtual bool overlaps(Shape& other) = 0;
};
class Circle;
class Square;
struct ShapeVisitor {
virtual void visitCircle(Circle& c) = 0;
virtual void visitSquare(Square& s) = 0;
};
// These three methods do the actual work
bool checkOverlap(Square& s, Circle& c) {
cout << "Checking if square overlaps circle" << endl;
return false;
}
bool checkOverlap(Square& a, Square& b) {
cout << "Checking if square overlaps square" << endl;
return false;
}
bool checkOverlap(Circle& a, Circle& b) {
cout << "Checking if circle overlaps circle" << endl;
return false;
}
class Square : public Shape {
struct OverlapVisitor : public ShapeVisitor {
OverlapVisitor(Square& _my) : result(false), my(_my) {}
virtual void visitCircle(Circle& c) {
result = checkOverlap(my, c);
}
virtual void visitSquare(Square& s) {
result = checkOverlap(my, s);
}
bool result;
Square& my;
};
public:
virtual void accept(ShapeVisitor& v) {
v.visitSquare(*this);
}
virtual bool overlaps(Shape& other) {
OverlapVisitor v(*this);
other.accept(v);
return v.result;
}
};
class Circle : public Shape {
struct OverlapVisitor : public ShapeVisitor {
OverlapVisitor(Circle& _my) : result(false), my(_my) {}
virtual void visitCircle(Circle& c) {
result = checkOverlap(my, c);
}
virtual void visitSquare(Square& s) {
// Important: note how I switched the order of arguments
// compared to Square::OverlapVisitor! There is only one
// square/circle overlap function checker, and it expects
// the square to be the first argument.
result = checkOverlap(s, my);
}
bool result;
Circle& my;
};
public:
virtual void accept(ShapeVisitor& v) {
v.visitCircle(*this);
}
virtual bool overlaps(Shape& other) {
OverlapVisitor v(*this);
other.accept(v);
return v.result;
}
};
Here is this running demo on ideone.
With RTTI approach you would make a map<pair<type_info,type_info>,checker> where checker is a type of a function that takes two pointers to Shape, and returns true or false depending on whether or not the shapes overlap. You make one such function for each pair of object types, populate the map with pointers to these functions based on type_info of their expected parameter types, and use this map at runtime to call the desired function.
Item 31 of the More Effective C++ book explains both these approaches in depth, with some great examples. In fact, the use case discussed in the book, detecting collisions between a pair of game objects, is similar to the one that you are implementing.
What you need is a "how big is other" type function. If we make it real simple, and just use a bounding box (a rectangle that is big enough to cover the entire shape), then we could do something like this:
(For simplicy, I'm using rect as a term for a rectangle)
class Shape
{
...
virtual rect BoundingBox() = 0;
bool overlaps(const Shape& other)
{
return BoundingBox.FitsInside(other.BoundingBox());
}
};
Obviously, you'll then have to write the function of fitsinside for two rectangles and BoundingBox for each shape, but it shouldn't be too hard.
To make a "is this Star completely covered by this Oval?" makes for a slightly more challenging solution [you will need to have a complete outline of both shapes, and an Oval outline may be quite a lot of points to be precisely oval].
Making the subclasses know about each other is a bad idea. If you want pixel-perfect collision, then you are going to have to iterate through every pixel in the shape and compare with the other shape's pixels. Create a virtual function to get a pixel N from the shape, where N is an index, and another function to return the number of pixels. For each pixel N in the current shape, compare with all pixels 0..Nmax in the other shape for collision.
The order of pixels from the index N can be any order. If you alternate pixels between different sides of the shape over nearby N, and start with the outer pixels first, you may be more likely to detect a collision on a lower N.
Now this simple approach is slow, especially if you have many shapes. The solution is to use a cheaper algorithm to check whether the perfect algorithm is necessary. A rectangle bounding box is the cheapest way. Work out the coordinates of a rectangle which is just large enough to hold your shape. I don't know how to work this out for a circle (geometry not my strong suit). You could even cache the bounding box sizes in the class to prevent recalculation for complex shapes. Checking whether two rectangles overlap is very quick and easy.
Only then move onto the costly algorithm if the bounding boxes overlap.
You can make faster checks between certain pairs of objects. For example, two rectangles overlap if their bounding boxes do. It's overkill to move onto pixel comparison. But you may not need this level of performance.
I'm writing an API for internal needs and so ease of use is one of the top priorities. I wonder if I'm pushing it too far as in the following example.
The function to solve the inverse geodetic problem takes in geocoordinates of two points and calculates distance between them and asimuths (angles) from each of the points to the other.
Ease-of-use-optimised conventional solution might look something like (please don't mind the length of the names, I know there's room for improvement):
class Angle;
class GeoCoordinate;
...
struct InverseGeodeticOut
{
Angle forwardAzimuth;
Angle backAzimuth;
double distance;
};
InverseGeodeticOut inverseGeodetic(const GeoCoordinate &firstPoint,
const GeoCoordinate &secondPoint);
// or
void inverseGeodetic(const GeoCoordinate &firstPoint,
const GeoCoordinate &secondPoint,
InverseGeodeticOut *result);
My question is how reasonable would it be to take it one step further to save user some typing:
class Angle;
class GeoCoordinate;
...
struct InverseGeodetic
{
InverseGeodetic();
InverseGeodetic(const GeoCoordinate &firstPoint,
const GeoCoordinate &secondPoint);
Angle forwardAzimuth;
Angle backAzimuth;
double distance;
};
// so the usages would be
InverseGeodeticOut inverse = inverseGeodetic(firstPoint, secondPoint);
InverseGeodeticOut inverse;
inverseGeodetic(firstPoint, secondPoint, &inverse);
InverseGeodetic inverse(firstPoint, secondPoint);
Perhaps in this particular example the difference is too small to be worth talking about but I wonder if such constructs are okay in general.
I like your 2nd code example, though I find the public constructor a bit confusing. Especially if there are other ways to construct an InverseGeodetic. I'd rather use a static factory method to construct it. That way you can give a more meaningful name to the method:
struct InverseGeodetic
{
Angle forwardAzimuth;
Angle backAzimuth;
double distance;
static InverseGeodetic FromTwoPoints(const GeoCoordinate &, const GeoCoordinate &);
};
// usage
auto inverse = InverseGeodetic::FromTwoPoints(a, b);
But then that might be my C# background bleeding through.
I am currently working on Chapter 7 in the book "Starting Out With C++ Early Objects" by the Pearson printing company.
I am unable to understand the function of the variable 'r' in this class declaration:
class Circle
{ private:
double radius;
public:
void setRadius(double r)
{ radius = r; }
double getArea()
{ return 3.14 * pow(radius, 2); }
};
Why can't I just write the 'radius' variable like this?
class Circle
{ private:
double radius;
double getArea()
{ return 3.14 * pow(radius, 2); }
};
I don't understand the function of the
public:
void setRadius(double r)
{ radius = r; }
Statement.
The technical reason is "because radius is private, hence inaccessible from outside the class".
So a change to radius must be some how managed by a public member function like SetRadius.
If the question now becomes "why designers did it that way, and did not simple make radius public?", well ... this is a never ending debate about how a proper object-oriented design should be an what has to be public and what not inside an object.
Traditional OOP school tends to make all data "private" and access or modify them through a variety of function to enforce what they call "encapsulation", and to have life easier in case the interface need to be extended to support eventual callbacks or events.
In this trivial simple case, well... all looks like a waste of energy (and without proper compiler optimization IS a waste of energy! In true physical sense). But may be they needed a uniform interface with something else.
As the functional behaviour of private is explained in other answers, directly accesing a private member outside the class will give you a compile-time error.
If you are asking why do we use setter functions and make some members private is a matter of design. For example; if you need the radius to be always positive number, you can write the set function as;
void setRadius(double r)
{
if(radius >= 0)
radius = r;
else
radius = 0;
}
Thus, you will have control over the values of the member when they are tried to be modified outside the class.
The radius is private. Without that function, you would not be able to set the radius from outside of the class. In order for the class to be useful, you would most likely want to be able to create objects of the type Circle and set their radius. Thus, you need some type of function in order to set that radius.
The easiest and most reasonable way to solve this is to supply a public member function inside the class Circle itself.
This can most easily be done using a setter, such as what you have shown. This allows you to set, and later change, the radius.
void SetRadius(float r)
{
radius = r;
}
You could also supply an extra argument to the constructor to ensure that a Circle always has its radius initialized with a user-supplied value, or at least set a default value in the declaration of radius (in C++11).
The concept of public private that no one can access the private variables just the class methods, and you can only access the public methods from your main function so ,the function setRadius is responsible to set the private variable of the radius
public:
void setRadius(double r)
{ radius = r; }
because the radius is a private variable so you have to create a public function inside the class to set that variable so you can set the radius when you create Circle object by :
this will work to set the radius in the main
Circle * test = new Circle;
test.setRadius(7);
but if you tried to set the radius directly by :
Circle * test = new Circle;
test.radius = 7;
it will crash the program Cannot access class private method