2

To be honest I do not know if the title is correct for the issue I am experiencing. The issue is thus. I have a class called Engine, of which there is one instance.

It contains two member variables (among others) called testTexture, an instance of my custom Texture class, and testObject, an instance of my custom object class.

In the Engine function Init their values are set thusly:

testTexture = Texture(0, TEXT("D:\\spriteWallVertical112.png"),
                      renderer.ReturnDevice());
testObject = Object(0,testTexture.textureID, D3DXVECTOR3(0,0,0),
                    D3DXVECTOR3(100,100,100), testTexture.texture, &renderer);

This all appears to function as I would want, their values are stored and appear to be maintained fine.

However, inside the Object class constructor there is a call to a function in my Renderer class called AddNewTextureObject:

rendererPointer->AddNewTextureObject(&objectID, &textureID, textureInput, 
                                     &origin, &coordinates);

This appears to go fine, but as the program runs the values the pointers appear to get overwritten as the program goes on. They don't instantly become junk memory, but it seems clear that they are. I can provide code as needed, but I don't want to just spam this question with code that isn't relevant to the question, especially if someone else may see something obvious that I am doing wrong.

I will however post the TextureObject class code for now as I think it's the most relevant here:

#ifndef TEXTUREOBJECT_H
#define TEXTUREOBJECT_H
#ifndef UNICODE
#define UNICODE
#endif

#include <d3dx9.h>

class TextureObject
{
public:
    TextureObject();
    TextureObject(unsigned int *, int *, LPDIRECT3DTEXTURE9, D3DXVECTOR3 *, D3DXVECTOR3 *);
    ~TextureObject();

    unsigned int *objectID; // The object with the texture.  Use this for locating and deleting this instance of TextureObject.
    int *textureID;
    LPDIRECT3DTEXTURE9 texture; // May not be needed if we can simply select the texture via ID.
    const D3DXVECTOR3 *origin; // Needed for drawing rotations....I think.
    D3DXVECTOR3 *coordinates;
    int maintainMe;
};
#endif

The variable maintainMe does keep its value if I assign to it.

This is the code for the AddNewTextureObject() function:

void Renderer::AddNewTextureObject(unsigned int *objectIDInput, int *textureIDInput, LPDIRECT3DTEXTURE9 textureInput, D3DXVECTOR3 *originInput, D3DXVECTOR3 *coordinatesInput)
{
    //testTextureObject = TextureObject(objectID, textureID, textureInput, originInput, coordinatesInput);
    testTextureObject.objectID = objectIDInput;
    testTextureObject.textureID = textureIDInput;
    testTextureObject.texture = textureInput;
    testTextureObject.origin = originInput;
    testTextureObject.coordinates = coordinatesInput;
    testTextureObject.maintainMe = 3067;

Note that either method of assigning values to testTextureObject results in the issue.

Any assistance with this would be greatly appreciated.

EDIT:

Here is the constructor for the Object class:

Object::Object(unsigned int objectIDInput, int textureIDInput, D3DXVECTOR3 originInput, D3DXVECTOR3 coordinatesInput, LPDIRECT3DTEXTURE9 textureInput, Renderer *rendererInput)
{
    objectID = objectIDInput;
    textureID = textureIDInput;
    origin = originInput;
    coordinates = coordinatesInput;
    rendererPointer = rendererInput;
    rendererPointer->AddNewTextureObject(&objectID, &textureID, textureInput, &origin, &coordinates);
}

It is declared in Object.h in the Object class as public like this:

Object(unsigned int, int, D3DXVECTOR3, D3DXVECTOR3, LPDIRECT3DTEXTURE9, Renderer *);

EDIT2: I made a copy constructor and an assignment operator:

Object::Object(const Object &source)
{
    objectID = source.objectID;
    textureID = source.textureID;
    texture = source.texture;
    origin = source.origin;
    coordinates = source.coordinates;
    rendererPointer = source.rendererPointer;
}

Object& Object::operator=(const Object &source)
{
    if(this == &source)
    {
        return *this;
    }

    objectID = source.objectID;
    textureID = source.textureID;
    texture = source.texture;
    origin = source.origin;
    coordinates = source.coordinates;
    rendererPointer = source.rendererPointer;

    return *this;
}

Do these look correct to you more experienced people? This alone does not appear to fix the issue unfortunately.

Interminable
  • 1,338
  • 3
  • 21
  • 52
  • Is there a reason you're passing references but the arguments to the method are pointers. I think there are use cases for both separately, but why mix them? – jedwards Jul 03 '12 at 23:30
  • I am afraid I do not quite follow what you're getting at. A pointer is a reference isn't it? – Interminable Jul 03 '12 at 23:34
  • 1
    In your `Engine`'s `Init` function, what exactly is happening with those `Texture` and `Object` instances? Are you accidentally creating them on the stack, not storing them permanently, having them disappear when `Init` completes and are being left with dangling pointers/references? – QuantumMechanic Jul 03 '12 at 23:34
  • Both `testTexture` and `testObject` are member variables of the `Engine` class. Engine is created in main so I do not believe anything is going out of scope. The values of `testTexture` and `testObject` appear to maintain the values they should. It's only the stuff in `testTextureObject` that appears to be going wrong. – Interminable Jul 03 '12 at 23:41
  • @Interminable a pointer is not a reference. A reference is an alias. You can create a reference to a pointer, or a pointer to a reference but their semantics are different. – Julien Lebot Jul 04 '12 at 00:32
  • Could you post the declaration (and possibly part of the definition) of Object's constructor ? – Julien Lebot Jul 04 '12 at 00:51
  • @LeSnip3R I have posted the things you asked for. – Interminable Jul 04 '12 at 07:29
  • That's a good start, unfortunately you are simply copying the pointers without thinking about what's going on with the memory, thus your copy constructor is no better than the (wrong) one provided by the compiler. You ought to make deep copy of the data (start here to see what is a deep copy http://www.learncpp.com/cpp-tutorial/912-shallow-vs-deep-copying/). Also I strongly recommend against storing pointers to D3DXVECTOR3, store the instance instead (D3DXVECTOR3 without the *). You typically want to operate on those as fast as possible, so better not scatter them in the heap. – Julien Lebot Jul 04 '12 at 12:11
  • @LeSnip3R The reason I was storing and passing pointers is because I wanted to be able to change the values for the `D3DXVECTOR3` and have the other values that use this update with it. – Interminable Jul 05 '12 at 07:35
  • It's common practice to use accessors to member variables, and not hand out pointers to internals; this break encapsulation. What you should do though, is pass around the Object instance, and whenever you need to change/see if the value changed, you use the accessors. This will give you a much better modularization of your program, and errors will be localized instead of spread throughout the program (e.g. how do you know if somebody messed with your pointers without accessors ? With accessors you can choose and restrict the access to the data). – Julien Lebot Jul 05 '12 at 16:21
  • @LeSnip3R I assume by pass around the object instance you mean pass around a pointer to it? – Interminable Jul 06 '12 at 07:50

1 Answers1

4

Since you define a destructor, and you have pointers in your TextureObject class, you need to follow the rule of 3: define a destructor, copy constructor, and assignment operator. It seems the pointers may have originated from Object, so you may need to do the same for that class as well.

I imagine the problem you are facing is a dangling pointer issue, in that after your initialization of testObject, the temporary used to initialize it destructs, and frees the pointers that were initialized within it. Thus, testTextureObject is now holding pointers to freed memory (because those pointers originally came from the temporary).

Edit: Based on the constructor for Object, we see that rendererPointer->AddNewTextureObject is being passed pointers from the current Object instance, which would be the temporary one.

testObject = Object(0,testTexture.textureID, D3DXVECTOR3(0,0,0),
                    D3DXVECTOR3(100,100,100), testTexture.texture, &renderer);

This line of code creates a temporary instance of Object, and then uses the assignment operator to initialize testObject. After this line of code, the temporary would be destructed. Now renderer is holding a TextureObject that is initialized to pointers from a temporary that no longer exists.

Edit: You seem to have some confusion about the problem that the rule of 3 is trying to help you resolve. You can read the the accepted answer to the question about the rule of 3. But to give you a simple example, just consider the simple problem of a class that allocates memory.

class Foo {
    Bar *bar;
public:
    Foo () : bar(new Bar) {}
    ~Foo () { delete bar; }
    Bar * get_bar () { return bar; }
};

The destructor is required to not leak memory. However, there are problems introduced if the copy constructor or the assignment operator is used.

Foo a;
Foo b(a); // copy

The problem with b is that it holds the same pointer as a. So when b and a are destructed, the pointer will get deleted twice.

Foo a;
Foo c;
c = a;    // assign

The problem with c is that not only is it holding the same pointer as a (which will lead to double deletion), but whatever memory it created in its constructor has now been leaked.

The rule of 3 is: If a destructor is needed, then so is a copy constructor and an assignment operator. The purpose of the rule is to make the developer think about the problems that needed to be solved by adding a destructor, and what consequences those would have for copy constructions and assignment, and create a reasonable solution.

In your case, renderer is holding a TextureObject created by the temporary Object. You need to think about how to fix the situation, either in the destructor, copy constructor and assignment operator of Object, or by avoiding the problem with some other solution.

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Thus the need to use new Texture(...) and new Object(...) instead of the stack allocated objects he used. – Julien Lebot Jul 04 '12 at 00:31
  • @LeSnip3R: Or a proper copy constructor/assignment operator that did a deep copy. – jxh Jul 04 '12 at 00:32
  • Mhhh unfortunately I don't think that's going to help solve his problem (though it is good programming advice). See the problem happens in the constructor of Object, and I think he takes some of the parameters by value (looking at the D3DXVECTOR in particular). Thus for AddNewTextureObject he would be passing pointers to local objects and that's probably where things go sour. Having a copy constructor wouldn't help there. – Julien Lebot Jul 04 '12 at 00:50
  • @LeSnip3R: He needs a copy constructor for `Object` as well. – jxh Jul 04 '12 at 00:52
  • Yes, you are right, but how is that going to help when you invoke UB by taking address of temporaries ? E.g. he creates a D3DXVECTOR and pass a copy to the constructor of Object, that constructor passes the address to AddNewTextureObject, constructor goes out of scope -> UB. I think we need to see that constructor to have a definite answer. – Julien Lebot Jul 04 '12 at 00:56
  • 1
    @LeSnip3R: If you are saying we don't have enough information to fully solve his problem for him, I agree. But, I think we have identified the cause of his problem, and he should be able to solve it for himself now that he knows where to look. – jxh Jul 04 '12 at 01:04
  • I was not aware I was taking the addresses of local variables. If I was, surely I would lose the original values in my instance of Object, which I do not? Also, in this answer, it describes a 'temporary [object] used to initialize it', how do you mean? Are you saying that the instance of `Object` is created, then copied to some other new object rendering the original pointers useless? Why would the function `AddNewTextureObject` called in the constructor for `Object` not pass the pointers for the newer instance of object and instead pass the ones for this temporary one? – Interminable Jul 04 '12 at 07:35
  • @Interminable: I have updated the answer based on the new information in your question. Regards – jxh Jul 04 '12 at 07:45
  • So what should I do about this copy-constructor in order to maintain the pointers? Or how do I try and deal with what I'm passing to the `AddNewTextureObject()` function? – Interminable Jul 04 '12 at 08:44
  • @Interminable: The solutions are implementation choices that you have to make. Possibilities are: (1) change `TextureObject` to make deep copies instead of accepting the pointers, or (2) don't call `AddNewTextureObject` from the constructor of `Object`, or (3) do not use a temporary to initailize `testObject`, or ... There are too many ways to skin this cat to list, the possibilities are large. You need to figure out what is the right way to do things for your program. The primary message is: obey the rule of 3. – jxh Jul 04 '12 at 08:51
  • Sorry, what I meant was when say creating a copy-constructor for the `Object` class, how do I preserve the memory location of the original data so that the pointers for the `TextureObject` instance do not become invalid? – Interminable Jul 04 '12 at 09:25
  • You need to copy the data, for instance if you have a D3DXVECTOR3*, you should not just copy the pointer, but its memory ! so *this->coordinates = *other->coordinates (because D3DXVECTOR3 has a copy constructor). For other data types, say arrays of basic types, you need to do memcpy(this->some_data, other->some_data, sizeof(basic_type) * num_elems); you would need to ensure that some_data has enough space allocated for the new data too. As user315052 pointed out, there are many ways to go about it. This article on wikipedia would be a good start http://en.wikipedia.org/wiki/Copy_constructor . – Julien Lebot Jul 04 '12 at 12:04
  • @Interminable: I have updated the answer with some additional advice. Regards – jxh Jul 04 '12 at 18:10
  • @user315052 In my `Object` class, it does not contain pointers. HOWEVER I want to maintain the memory addresses of the ORIGINAL variables from the temporary object for when the new object is made. ie instead of having `objectID = source.objectID` assign a new memory address for `objectID`, I want `objectID` to use the memory address assigned for the temporary. Thus the memory addresses the `textureObject` is pointing to do not become invalid. Can this be done? – Interminable Jul 04 '12 at 21:22
  • @Interminable: If I understand your request, it defies the meaning of temporary. You can instead encapsulate state within `Object` that is dynamically allocated, and hand this internal state over. If you use a smart pointer to manage the internal state, then `Object` would not need a destructor. – jxh Jul 05 '12 at 01:28
  • How do I go about encapsulating the state as you describe? I'm not really too familiar with 'smart pointers'. Also I have been able to work around the issue by having a function in the `Renderer` class that updates the contents of the appropriate `TextureObject`. It's called from the assignment operator for `Object`. – Interminable Jul 05 '12 at 08:51
  • @Interminable: That is another way to go. Encapsulation would be another object called `ObjectData` that has all the data members that is currently in `Object`, and `Object` would only have a smart pointer to `ObjectData`. Google for `C++ shared_ptr` for an example of a smart pointer. – jxh Jul 05 '12 at 08:55