I have just switched from Java to C++ and everything is going pretty well so far. The language is kind of hard, but I feel as though I am catching on. I have a question about destructors though, and for this I will supply my current code in hopes that someone could provide some clarification on what and how I should proceed.
I am writing a game in OpenGL and have created Three classes: Vector3D, Dimension3D, and Cuboid. My problem starts like this, my cuboid has an instance of both Vector3D and Dimension3D, which you will soon see. I need to know how (the exact routine) of what happens to my Cuboid class once it is flagged for deletion. Or more precisely, if i need to explicitly destroy both the Vector3D and Dimension3D instance when such an event occurs. I hope I articulated this question adequately enough.
Here are my classes.
(Vector3D.cpp)
#include "Vector3D.h"
Vector3D::Vector3D(){
}
Vector3D::Vector3D( const float& x , const float& y , const float& z ){
this->x = x;
this->y = y;
this->z = z;
}
Vector3D::~Vector3D(){
}
void Vector3D::setX( const float& x ){
this->x = x;
}
void Vector3D::setY( const float& y ){
this->y = y;
}
void Vector3D::setZ( const float& z ){
this->z = z;
}
float Vector3D::getX(){
return x;
}
float Vector3D::getY(){
return y;
}
float Vector3D::getZ(){
return z;
}
(Vector3D.h)
#ifndef NE3_Vector3D_H_
#define NE3_Vector3D_H_
class Vector3D{
public:
Vector3D();
Vector3D( const float& , const float& , const float& );
~Vector3D();
void setX( const float& );
void setY( const float& );
void setZ( const float& );
void setPosition( const float& , const float& , const float& );
float getX();
float getY();
float getZ();
float x;
float y;
float z;
private:
// Private Members Go Here
};
#endif // NE3_Vector3D_H_
(Dimension3D.cpp)
#include "Dimension3D.h"
Dimension3D::Dimension3D(){
}
Dimension3D::Dimension3D( const float& width , const float& height , const float& depth ){
this->width = width;
this->height = height;
this->depth = depth;
}
Dimension3D::~Dimension3D(){
}
void Dimension3D::setWidth( const float& width ){
this->width = width;
}
void Dimension3D::setHeight( const float& height ){
this->height = height;
}
void Dimension3D::setDepth( const float& depth ){
this->depth = depth;
}
float Dimension3D::getWidth(){
return width;
}
float Dimension3D::getHeight(){
return height;
}
float Dimension3D::getDepth(){
return depth;
}
(Dimension3D.h)
#ifndef NE3_Dimension3D_H_
#define NE3_Dimension3D_H_
class Dimension3D{
public:
Dimension3D();
Dimension3D( const float& , const float& , const float& );
~Dimension3D();
void setWidth( const float& );
void setHeight( const float& );
void setDepth( const float& );
void setSize( const float& , const float& , const float& );
float getWidth();
float getHeight();
float getDepth();
float width;
float height;
float depth;
private:
// Private Members Go Here
};
#endif // NE3_Dimension3D_H_
And lastly, my Work in progress Cuboid.cpp and Cuboid.h
(Cuboid.cpp)
#include "Cuboid.h"
Cuboid::Cuboid(){
}
Cuboid::Cuboid(const Vector3D& location, const Dimension3D& dimension){
this->location = location;
this->dimension = dimension;
}
Cuboid::~Cuboid(){
// Do i do delete both location and dimension here?
}
void Cuboid::drawImmediate(){
}
void Cuboid::drawVBO(){
}
(Cuboid.h)
#ifndef NE3_CUBOID_H_
#define NE3_CUBOID_H_
#include "Vector3D.h"
#include "Dimension3D.h"
class Cuboid{
public:
Cuboid();
Cuboid( const Vector3D& , const Dimension3D& );
~Cuboid();
void drawImmediate();
void drawVBO();
Vector3D location;
Dimension3D dimension;
private:
};
#endif // NE3_CUBOID_H_
I left a comment in Cuboid.cpp within the destructor. I want to know if I should be deleting the Vector3D and Dimension3D there, and an example showing the best way to do this. IE: Any common conventions that express this functionality.
If my question is not adequate, I will be more than happy to provide further clarification. Also, I am sure that there are other questions like this, however, I need to see it in my own code to fully grasp it. (weird learning style), so please forgive me if this turns into a duplicate.
In this particular case you do not need any explicit destruction code.
The reason for this is that you are using direct members:
class Cuboid {
public:
Vector3D location;
};
This means that the Vector3D object is embedded into the memory of the Cuboid and automatically allocated and released together with the Cuboid.
It would be a different case if you had a pointer to the object as member (e.g. Vector3D *location) instead of the member itself. In that case, you would also have to explicitly allocate memory for the location, as well as explicitly release it in the destructor.
Since you are just starting with C++, a beginner rule of thumb that you can use is that you explicitly delete only what you explicitly allocate.
Since you haven't allocated the Vector3D yourself (for example via new), you shouldn't delete it.
The situation would have been different, if your code looked like this,
// Cuboid.cpp
Cuboid::Cuboid(){
location = new Vector3d;
}
Cuboid::~Cuboid(){
delete location; // now this is necessary
}
// Cuboid.hpp
class Cuboid {
private:
Vector3D* location; // using a raw pointer here, this can be different for scoped/RAII pointer, unique_ptr
};
Also, consider making the members private, encapsulation is important in C++ as much as it is in Java.
Then as you progress with C++ you will encounter RAII, a slightly more advanced topic, that invalidates the rule of thumb above in the sense that you will use the language constructs, or your own scoped/RAII classes to handle the deallocation for you in a deterministic way. For example, in the following case,
std::unique_ptr<Vector3D> location(new Vector3D);
you won't need to deallocate location yourself, it will be automatically deallocated by the C++ standard library when the unique_pointer will go out of the current scope block, or when the enclosing object, of which location is a member, will be deallocated.
If you allocated the Vector3D object and the Dimension3D object dynamically in the constructor of class Cuboid, then you would need to delete them in the destructor of class Cuboid:
Cuboid::Cuboid(const Vector3D& location, const Dimension3D& dimension){
this->location = new Vector3D(location);
this->dimension = new Dimension3D(dimension);
}
Cuboid::~Cuboid(){
delete location;
delete dimension;
}
But since this is not the case in your code, you do not need to delete these objects.
Whether you allocate a Cuboid object dynamically with Cuboid* x = new Cuboid(...), or statically with Cuboid x(...), these two member objects (location and dimension) will be implicitly destroyed as soon as the (empty) destructor of class Cuboid is invoked.
Cuboid's location and dimension members do not need to be deleted in its constructor. Their destructors are invoked automatically when a Cuboid instance is destructed since you are not explicitly managing their memory allocation, i.e. you didn't use "new" when creating them.
Further, given the code you've shown, both the Vector3D and Dimension3D classes don't need to do any manual resource management in their destructors either. Basically, if you aren't using new to allocate memory for any objects you typically don't need to worry about releasing them.
No you do not need to deallocate the members location and dimension as they are automatic objects whose lifetimes is automatically controlled. They will automatically be destroyed when the class Cuboid is destroyed.
It is in fact undefined behaviour to perform a delete on e.g. a pointer to a local automatic object.
As a best practice you should not use delete in C++ (unless you know what you're doing). If you need to dynamically allocate memory (and the containers in the standard library does not do the job) then you should use a smart pointer, as std::shared_ptr, that can handle the deallocation for you (see RAII).
Related
I have a class Particle:
class Particle {
private:
float x, y, z;
// ...
public:
// ...
float* getPos() {
float p[3] = {x, y, z};
return p;
}
// ...
};
I would call this method like:
Particle a = Particle();
// ...
float* pos = a.getPos();
And then reference the position elements with pos[0] through pos[2].
g++ spouts warning message as stated in the title. But the functionality is exactly how I want it: returning an array. Why does the warning exist and is there a "proper" way to do it?
You can't return a C-array like that, return std::array instead:
std::array<float, 3> getPos() {
std::array<float, 3> p = {x, y, z};
return p;
}
You'll need to include <array> for that.
Personally, I'd skip std::array/std::vector here, because in your particular case, the position of each value imposes independent meaning. In general, sequence types have ordering, tuples have structure; if the element count is fixed (and often heterogeneous) and sorting (or otherwise reordering the values) is intrinsically nonsensical (e.g. in the case of a coordinate, swapping the x and y values changes the meaning), then a tuple makes more sense.
In this case, you could just declare:
std::tuple<float, float, float> getPos() {
// C++17 or higher allows list initialization
return {x, y, z};
// Pre-C++17 you use the std::make_tuple helper
return std::make_tuple(x, y, z);
}
The advantage here is that you can then unpack the result in the caller easily, either with std::tie:
float x, y, z;
std::tie(x, y, z) = a.getPos();
or on C++17 or higher with structured bindings, it's even nicer, since you can declare and initialize the variables with auto, rather than declaring with explicit types, then reassigning with tie:
auto [x, y, z] = a.getPos();
You can store the tuple itself and use std::get if you prefer, but unpacking to useful names rather than obscure std::get indices usually makes for much cleaner code.
You're not returning an array. It's impossible to return an array in C++. You're returning a pointer to an array which no longer exists. Hence the warning.
You could make the array a part of your class and return a pointer to that. In general I wouldn't call that good design
class Particle {
private:
float pos[3];
// ...
public:
// ...
float* getPos() {
return pos;
}
// ...
};
You could return a vector<float> instead. You could return an array<float,3> instead. You could ask yourself why you need this.
p[3] will be destroyed when it goes out of scope so you shouldn't return a pointer to it.
Either return a std::array<float, 3> by value or consider making a class for positions too, and return a Position object, or a reference to it. Example:
struct Position {
float x, y, z;
};
class Particle {
private:
Position m_pos;
// ...
public:
// ...
Position const& getPos() const { return m_pos; }
// ...
};
I'd suggest that you're function is indicative of poor design. Provide getter methods an allow the user of your class to access member variables:
class Particle {
private:
float x, y, z;
public:
float GetX() const { return x; }
float GetY() const { return y; }
float GetZ() const { return z; }
};
Given const Particle a this will let you initialize an array as follows: const float pos[] = { a.GetX(), a.GetY(), a.GetZ() }
Creating a Particle method to allow the user to populate a float[] will encourage the user toward one of the following bad practices:
float* Particle::GetPos() const { return new[3]{ x, y, z }; } creates dynamic memory without clearly informing the caller that the memory needs to be released
array<float, 3U> Particle::GetPos() const { return { x, y, z }; } requires the allocation and creation of a temporary to populate a float[]
void Particle::GetPos(float* param) const { param[0] = x; param[1] = y; param[2] = z; } misses the opportunity for constant arrays and incurs potential caller misuse, as it's not clear that param must have room for at least 3 floats
I want to have a class which has a member array. The size of the array should be given when I initialize the object. I just found a way with pointers to do this. I think it is working correctly, but can you maybe tell me if this is the best way to do it or if there are any things which do not work which I haven't recognized yet?
#include <iostream>
using namespace std;
class Surface {
private:
float dx;
int N;
float* mesh_points;
public:
Surface(float, int);
~Surface();
void set_dx (float);
float get_dx();
};
Surface::Surface(float dx,int N){
this->dx = dx;
this ->N = N;
mesh_points = new float[N];
}
void Surface::set_dx (float dx) {
this->dx = dx;
}
float Surface::get_dx (void) {
return dx;
}
Surface::~Surface(){
delete[] mesh_points;
}
int main () {
Surface s(1.2,10);
s.set_dx (3.3);
cout << "dx: "<< s.get_dx() <<endl;
float mesh_points[3];
return 0;
}
can you maybe tell me if this is the best way to do it or if there are any things which do not work which I haven't recognized yet?
That'd be my take basing on existing best practices:
class Surface {
private:
std::vector<float> mesh_points;
public:
float dx;
Surface(float dx, std::size_t n);
};
Surface::Surface(float dx, std::size_t n)
: dx(dx)
, mesh_points(n)
{
}
In short, the changes made:
Got rid of manual memory management, which implies dtor as well.
Added names for parameters in declarations (really important for usability/IDEs, don't remove them!)
Got rid of superfluous accessors and made dx public.
Used ctor init lists, making the body obsolete.
Got rid of using namespace std; in lieu of explicit std:: prefix.
Changed n type to std::size_t (see comment).
Please note that the current interface doesn't allow any access to mesh_points.
Here's an alternative suggestion that allows you to keep your current implementation but is a lot safer.
class Surface {
private:
float dx;
int N;
float* mesh_points;
public:
Surface(float, int);
~Surface();
void set_dx (float);
float get_dx();
Surface(const Surface&) = delete; // new
Surface& operator=(const Surface&) = delete; // new
};
By deleting the implementation of the copy constructor and copy assignment operator you prevent your Surface objects from being copied (which would likely crash your program anyway). Any attempt to copy Surface objects will now result in a compile time error.
Only a suggestion, my first choice would always be to use std::vector.
I have a simple point class:
class Point {
public:
Point(const double, const double);
/** This constructor creates invalid point instance **/
Point();
~Point();
const double x;
const double y;
/** This returns true if one of the values is NaN **/
bool isInvalid() const;
/** Returns true if coordinates are equal **/
bool equal(const Point& p) const;
};
Values x and y are const so that I can be sure they never change. They are supposed to be always constant. The problem is I can't assign to variables holding Point:
Point somePoint;
... meanwhile, things happen ...
//ERROR: use of deleted function 'Point& Point::operator=(const Point&)'
somePoint = Point(x, y);
I understand that assigning is a problem because somePoint.x = something is forbidden. I need to use point to hold last point value during rendering:
Point lastPoint;
PointInGraph* point = graphValues.last;
while((point = point->next())!=nullptr) {
// calculate pixel positions for point
double x,y;
...
if(!lastPoint.isInvalid())
drawer.drawLine(round(lastPoint.x), round(lastPoint.y), round(x), round(y));
// ERROR: use of deleted function 'Point& Point::operator=(const Point&)'
lastPoint = Point(x, y);
}
So does const in class properties simply make any variable of that class type const as well? Or is there a workaround?
It's not possible. It would require modifying const values.
Instead of making x and y themselves const, make them non-const but provide a const interface to them, i.e. by making them private and providing const getters.
Instead of making the variables const you could just not provide any way for the user to change the values by:
making them private
only allowing assignment to a another instance as the only way to change the values.
You can see how this would work in the following example:
#include <iostream>
class Point {
public:
Point(const double x_ = 0, const double y_ = 0) : x(x_), y(y_) {}
double getX() const { return x; }
double getY() const { return y; }
private:
double x;
double y;
};
int main()
{
Point a{1,5};
Point p;
p = a;
std::cout << p.getX() << ", " << p.getY(); // no error here
//p.x = 5; // error here now
//p.y = 7; // error here now
}
Live Example
If you un-comment the last two lines you will get an error to prove that you cannot change x and y.
Actually it's, of course, possible by directly technique called cast away constness which is a known loophole in const mechanic in C++. Basically I can abuse the fact that const int* can be assigned to int* variable. The possible solution:
Point& Point::operator=(const Point& p)
{
*((double*)&x)=p.x;
*((double*)&y)=p.y;
}
This is of course not recommended, but I'm not the one of the people who think that knowledge is dangerous.
I have this code:
class Vector3
{
public:
Vector3() : x(values[0]), y(values[1]), z(values[2])
{ x = y = z = 0; }
float& x;
float& y;
float& z;
private:
float[3] values;
};
class Model
{
public:
Vector3 vertices[64];
};
I'm doing this vector class because I want to deal with the values as X, Y, Z in the code, but for some operations I need a contiguous array of values to be passed to a function.
So the whole array of vertices[64] need to be [x0][y0][z0][x1][y1][z1][x2][y2][z2] etc.
But if I do this:
//Get first address:
void* firstAddress = &vertices[0];
//Or
void* firstAddress = vertices;
I don't have the contiguous array as I need it (the data is all messed up), and I'm guessing it's because of the pointers I have in the Vector3 class.
Is there any way I can do get this functionality that I want? (Having a single array of float but dealing with values as x,y,z)
Firstly, the Standard doesn't define how references should be implemented, but they'll almost certainly occupy actual memory in your class much a pointer members would, ruining the contiguous data packing you're hoping for....
If your focus is more on the vertices container, and you just want x/y/z member access to elements in it, then you could try something like:
template <size_t N>
class Vertices
{
public:
class Proxy
{
public:
Proxy(float* p) : x(p[0]), y(p[1]), z(p[2]) { }
float& x;
float& y;
float& z;
};
Proxy operator[](size_t n) { return Proxy(&d_[n * 3]); }
const Proxy operator[](size_t n) const { return Proxy(&d_[n * 3]); }
private:
float d_[N * 3];
};
You could have member functions instead:
class V3
{
float data[3];
public:
V3() : data{0,0,0} {}
float & x() { return data[0]; }
float & y() { return data[1]; }
float & z() { return data[2]; }
};
You could also omit the constructor and have an aggregate, if that's more suitable.
What you have and array of values pointers (pointing to arrays of 3 floats) with 3 float references (x, y, z). You probably ant something more like:
float & x() { return values[0]; }
float & y() { return values[1]; }
float & z() { return values[2]; }
If I understand your requirement for continuous data in the Model, then your Vector is really an alias to a specific triple of data in the model. No float storage required.
class Model
class Vector
Vector(Model *model_, size_t idx_) : model(model_),idx(idx_) { };
Model *model;
size_t idx;
float & x() { return model->data[3*idx]; }
float & y() { return model->data[3*idx+1]; }
float & z() { return model->data[3*idx+2]; }
float data[64 * 3];
Vector vectors[64];
Model() {
...
for( size_t ii = 0; ii < 64; ii++ ) {
vectors[ii] = new Vector(this,ii);
}
Vector & vector(size_t idx) { return vectors[ii]; }
For a guaranteed contiguous array you need to either copy the data into the array, or use compiler-specific guarantees about memory layout, in particular that
there will be no padding,
and if you want to also access triplets of the array as Vector3 instances, that
accessing a Vector3 at arbitrary address will not cause a trap or inefficiency (we're into alignment here).
It so happens that some common libraries such as OpenCV do make such assumptions for their internal image buffers.
But I'm not entirely sure that the code I've seen has not been platform-adapted. So, in practice you have these choices:
copy the data contiguously to an array (or from it), and/or
use an existing library that provides this kind of functionality, such as OpenCV.
Note that using member functions instead of references buys you nothing wrt. to the contiguous array problem, but it does make the Vector3 potentially assignable.
On further reflection, i was maybe too trigger-happy writing the above. For if you can guarantee total size 3*sizeof(float), and that's almost a given (just get rid of those references), then you are guaranteed that you can access a Vector3 at any address that can hold a float, since C++ guarantees arrays with no padding, and since in such an array a Vector3 can end up at any address that can hold a float. So the in-practice problem reduces to making a decision about supporting compilers or compiler configurations that are unable to make Vector3 of size 3*sizeof(float).
I.e.
struct Vector3
{
float x, y, z;
auto operator[]( int i ) -> float& { return (&x)[i]; }
auto operator[]( int i ) const -> float const& { return (&x)[i]; }
};
static_assert( sizeof( Vector3 ) == 3*sizeof( float ), "Ungood Vector3 size" );
using the fact that members with no intervening access specifier are guaranteed to be in increasing address order.
Disclaimer: off the cuff code, not touched by compiler's hands.
If you keep your Vector3 class a POD you should be able to simply cast your vertices to a float array:
struct Vector3 {
float x;
float y;
float z;
};
class Model
{
public:
Vector3 vertices[64];
float* data() {
return reinterpret_cast<float*>(vertices);
}
};
int main() {
Model m;
for(int i = 0; i < 64; ++i) {
m.vertices[i] = {10+i,100+i,1000+i};
}
float *data = m.data();
for(int i= 0; i < 64*3; ++i) {
std::cout << data[i] << ", ";
}
}
Only problem could be the Allignment of the structure, but should you use c++11, there is a standard way of alligning the structure, using allignasalignas(alignof(float[3])). I don't know if this is really required.
Also, c++11 gives quite a few options on what to do with Vector3, while still considering it as a POD type.
I wonder if it exists a way to use a class and a float pointer interchangeably. Lets say a class is basically an array of doubles (fixed size). If I have the class pointer, I can use it as a float pointer (easy with the appropriate operator), however, if I have the pointer, I don't know how to automatically use it as a class.
Let me explain a bit more my problem.
I've been using a Matrix4x4 typedef to hold a 4x4 matrix:
typedef float Matrix4x4[16];
I have lots of functions that take Matrix4x4 as a float*
Now I'm trying to use a basic class the same way I used to use the Matrix4x4:
class Matrix4x4 {
float matrix[16];
public:
Matrix4x4();
float operator[](int i){
return matrix[i];
}
operator float*() const{ // I can pass to functions that take a float*
return (float*) matrix;
}
};
The problem remains when I need to call a function like this one:
bool test(void){
float H[16];
// ... process H
return isIdentidy( H); // I want the compiler to accept it this way
return isIdentidy((float*) H); // or this way
}
bool isIdentity(const Matrix4x4 matrix){
... (process)
return ...;
}
At the very end, the pointer should be the same right?
(if I declared H as Matrix4x4 H instead of float H[16]
Is there a way to accomplish this without having to use static_cast or dynamic_cast?
Thank you very much
There isn't a way to do what you want, but you can do something very similar.
First make a new constructor for Matrix4x4 that accepts a float[16] argument
class Matrix4x4 {
float matrix[16];
public:
Matrix4x4();
Matrix4x4(float values[16])
{
memcpy(matrix, values, sizeof(float)*16);
}
float operator[](int i){
return matrix[i];
}
operator float*() const{
return (float*) matrix;
}
};
Then you can do
bool test(void){
float H[16];
// ... process H
return isIdentidy(Matrix4x4(H));
}
bool isIdentity(const Matrix4x4 matrix){
... (process)
return ...;
}
Any changes to the new Matrix4x4 will be lost though.