Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


rip-off

Member Since 16 Mar 2005
Offline Last Active Yesterday, 02:27 PM

#5213660 I think I don't understand dynamic memory

Posted by rip-off on 01 March 2015 - 08:55 AM

I don't know if this constitutes a "theory", but maybe a good way to answer your qusetion is to illustrate some common scenarios, with examples, where a programmer might reach for dynamic memory allocation.

The programmer cannot predict the number of objects to run computations at compile time. Knowing the exact number of objects in advance is rare, in fact. There are two primary exceptions:
  • The domain itself has hard limits in place. For example, Chess is played on an 8 x 8 board with 16 pieces per player, so a program for playing chess can avoid dynamic allocation for storing the state of the board. However, the number of moves during the game is not known in advance, so dynamic memory would be required to provide a full game log.
  • The designer chooses an arbitrary limitation on some aspect of the program. This can be for a number of reasons, including not wanting to design a flexible UI that can cope with arbitrary amounts of data (thus avoiding complex workflows like search, sort and paging) or to achieve soft real time performance goals( in a game context this could be trying to ensure that no frame takes more than 16.6 milliseconds).
Another reason is that the lifetime of an object is unknown at compile time. It will almost certainly outlast the function that creates it, it may even outlast the object or objects it is first stored in. An example might be a particular map texture in a multi-player FPS client. Provided the server's map rotation continues to require that texture, it would be preferable to avoid unloading and reloading it. However, once the texture is no longer required, it can be remove to make way for the new textures the next map needs instead. Thus any given texture might be stored for the duration of one map, or several.

Another is the behaviour of the object cannot be precisely determined at compile time. Most object oriented languages implement polymorphism by allocating a dynamic amount of memory, and returning some kind of reference to this memory. Other parts of the code might take a more generic reference, e.g. a pointer to a super class in C++, and without knowing the exact size or layout of the object it can be made use of. Some simple games can be implemented such that every game object inherits from a very simple base class with a virtual function for updating itself (e.g. Asteroids). Most people find that this approach typically doesn't scale well once the number of object types starts to grow.

Finally, a programmer may be forced to use dynamic memory because the language or library they are using make use of it. In particular, it is common in C APIs not to expose the definition of a data structure, which provides maximum freedom for the library implementor when making changes. For example, the Simple Directmedia Library (SDL) is a C API which does this. Some of the API calls wouldn't necessarily require dynamic amounts of memory, but by hiding the details the library implementation can change the size of it's data structures without changing the public API. It also makes the API consistent, as now there is a common pattern for interacting with all parts of the API.


#5213502 I think I don't understand dynamic memory

Posted by rip-off on 28 February 2015 - 10:21 AM

Dynamic memory means that the memory lifetime isn't managed automatically by the language - like how when you call a function, memory is automatically provided for the various local variables you need.

Dynamic memory is, by default, managed explicitly by the programmer. This is the familiar new / delete or new[] / delete[] calls.

However, by using the other features of the language, namely destructors, this can be automated again - the explicit memory management is done by the implicitly called destructors. So the std::make_shared<> will dynamically allocate the object, and the destructor deallocates it.

Now, to come to your Student class. It may in fact use dynamic memory, as std::string is dynamically sized (though some implementations can avoid allocations for short strings). However, this is irrelevant, as any object can be dynamically allocated - you can dynamically allocate an "int" if you want.


#5212291 which psyhics engine should i use for arhery game in java?

Posted by rip-off on 22 February 2015 - 11:19 AM

Can you tell us a little more about your game? What are your requirements? Is it going to be a realistic simulation game, or would a good-enough "arcade" feel work fine?


#5210138 Exception while loading a file with content in Java

Posted by rip-off on 11 February 2015 - 04:32 PM

In addition, your code doesn't close the scanner in the event of an exception.

Note that in recent versions of Java you should be able to write:
try (Scanner loadScanner = new Scanner(loadPath)) {
    // Use the scanner
} catch (IOException e) {
    e.printStackTrace();
}
This is called "try with resources", and the resource specified is automatically closed, whether an exception occurs or not - you don't have to write such code yourself.


#5210029 Sending Messages To Multiple Clients

Posted by rip-off on 11 February 2015 - 08:16 AM

You're using threading, are you sure the object's lifetime persists until the callback executes? How are you ensuring your "Clients" collection is not being accessed by two threads at once?




#5210006 Exception while loading a file with content in Java

Posted by rip-off on 11 February 2015 - 06:17 AM

The error is thrown by the Scanner. Can you post the file you're using?




#5209411 Does games that were writen in "c" will be processed faster then...

Posted by rip-off on 08 February 2015 - 08:38 AM

If you're posting in "For Beginners", the choice of programming language will certainly not be the thing that limits your game's performance. To get maximum speed from a low level systems language requires lots of experience and understanding of how modern machines work. For lots of programs, getting the correct algorithms and data structures is far more important and far more likely to be the bottleneck.

Low level languages like C and C++ are extremely unforgiving to beginners, as they were designed with the maxim "The programmer is always right". Even master programmers make mistakes on occasion, but beginners are certain to make lots of mistakes that are going to be frustrating to understand, debug and finally fix.

As mentioned, the biggest risk to a project is that it will not be completed - and choosing a complicated and challenging language certainly increases that risk. Again, as a beginner, you might be better off making a few smaller games first to gain experience - not trying to take on something that will push computers to their limits. Think smaller games, maybe 2D, not necessarily the larger 3D ones you want to sell. Once you've built up the basic skills you need, then tackling that bigger, more commercially viable, game becomes a more realistic goal, and you'll do a far better job having learned some necessary lessons in those smaller, safer ventures that you got out of the way first.

To be absolutely clear: if you're not sure what language to use, it is clear that you haven't yet built up the necessary skills to develop and release a real, polished, commercially viable game. So focus on building up those skills now.

You might be asking the wrong question. Consider experimenting with existing technology, rather than making a large investment building your own. You will likely be far more productive scripting something like Unity than trying to build a game from scratch using, for example, C or C++ and OpenGL. You can lean on the expertise of the engine programmers, and really concentrate on building a compelling game.


#5208392 SDL2 and Linux [RANT]

Posted by rip-off on 03 February 2015 - 07:41 AM

Generally, I've found SDL to be a fantastic piece of software.




#5208276 Deleting from the Destructor on Out of Scope.

Posted by rip-off on 02 February 2015 - 03:58 PM

Have you considered using std::vector<char> or std::string?

Unfortunately, I'm not very familiar with C++11, so I'll refrain from making suggestions there. However, whatever you do, you should ensure your class obeys the Rule of Three, so that you don't accidentally use incorrectly generated copy constructors or assign operators.

On a side note, it is safe to delete or delete[] a null pointer.


#5207845 Low Level Memory Management C++

Posted by rip-off on 31 January 2015 - 06:44 AM

Your smart_array assignment operators appear to be incorrect, it doesn't decrement the reference count of the current object (if any). Also, I think it is surprising and not advisable to support arbitrary pointer identity comparison. I'd recommend instead having an explicit member function if this is actually required. The detach member function seems poorly designed, it doesn't guarantee ownership transfer (as other references may still exist), so the client cannot safely assume ownership of the returned pointer.

As for the ArrayList, the class is certainly not thread safe. Generally, it is really hard to provide thread safety at such a low level, as most client code will need to make multiple calls to the object to achieve many tasks. If you wish to provide a thread safe collection, I'd recommend a separate class (possibly making using of this class, or std::vector<> internally) with a limited, "transactional" oriented interface. The limited interface makes it easier to validate the thread safety. In addition, you are using a mixture of low and high level thread primitives, I'd recommend picking one (e.g. mutex locking) and using it consistently (e.g. the entry point to every public member function).

Other than that, I find many of the member functions to be very surprising. For example, the difference between size() and count() is very subtle and I would say unnecessarily error prone. The behaviour of resize() is bizarre, it does nothing if the array is smaller than specified, otherwise it expands the array by the number of elements passed.

In any case, one issue is that the smart_array assumes it can delete[] the pointer it manages, but the wrapping ArrayList is giving it pointers that were not allocated with new[].

Overall, I find the classes a confusing mix of thread-safety, reference counting, memory management and dynamic array logic. As mentioned above, the first thing I would recommend is removing the thread safety from the equation. You can write a thread-safe wrapper for this later - if it is really required! Ideally, you try not to share objects between threads when you can avoid it, the only objects that should be shared are thread-oriented things like work queues for producer / consumer relationships. I'd also recommend dropping the reference counting, again if required this can be handled at a higher level.

Once you've dropped these, then it becomes clear that you're essentially re-implementing std::vector. Consider not doing that, but instead using the tried and tested implementation. You can then wrap something like that in a thread safe interface, if that is really something your program requires.


#5207410 Is it a very bad idea for a class to pass (*this) into a function? Strange bu...

Posted by rip-off on 29 January 2015 - 05:38 AM

The solution is to understand the Rule of Three (this rule has recently grown a bunch of different names, but I think this still is the best name).

 

So, your BitmapFont class or potentially some of the other classes it uses, should be marked as "noncopyable". You can achieve that in C++11 by "deleting" the copy constructor and assignment operator, and in pre C++11 you can simulate this by declaring them private and not implementing them.

 

This will address the issue you mentioned of the silent bug lying around. Anywhere you are copying these objects should now give a compilation error (or possibly a link error).

 

The next thing to consider is the design. As Washu says, this design seems weak.




#5207253 Is it a very bad idea for a class to pass (*this) into a function? Strange bu...

Posted by rip-off on 28 January 2015 - 02:08 PM

What is likely happening is that the BitmapFont is not copyable. That is, when you create a copy, and the copy goes out of scope, it destroys resources that the "original" is also using.

Something like this:
$ cat test.cpp
#include <iostream>
class Bad {
public:
    Bad() : pointer(new int(42)) {
        std::cout << "constructor: " << this << ", pointer: " << pointer << '\n';
    }

    ~Bad() {
        std::cout << "destructor: " << this << ", pointer: " << pointer << '\n';
        delete pointer;
    }

    int *pointer;
};

int main() {
    Bad original;
    Bad copy = original;
}
$ g++ test.cpp && ./a.out
constructor: 0x7fffeb5038a0, pointer: 0x131d010
destructor: 0x7fffeb5038b0, pointer: 0x131d010
destructor: 0x7fffeb5038a0, pointer: 0x131d010
*** Error in `./a.out': double free or corruption (fasttop): 0x000000000131d010 ***
Aborted (core dumped)
As you can see, there are two "this" pointer values, but only one "pointer" value. The destructor causes the pointer in "original" to also be invalidated.


#5206863 Designing a < operator for a GUID

Posted by rip-off on 27 January 2015 - 01:56 AM

You don't need GUIDs for this. GUIDs are useful if two machines that aren't currently connected together need to independently generate identifiers, which later might become connected later (and the IDs could collide).

In most networked games, this isn't the case. The computers are immediately connected, so the server can generate IDs and send them to the client.

When sending binary data across the network, you do have to be careful. You have to pay attention to the size of each field, and any padding. In addition, a binary integer has many representations, the most common of which are little endian and big endian. One typically selects an endianness for the protocol, and converts incoming multi-byte integers to that storage. Historically, big endian has been preferred, even though on modern desktops you are likely using little endian. Some network protocols use different techniques, e.g. variable length encoding depending on the value, or send a delta-value instead, possibly with run-length encoding (RLE) to handle blocks of zero values.

Sending text data doesn't have these worries, but you have other issues about parsing such as handling delimiters that occur in the data you want to send, e.g. if you use the space character as a delimiter but the user wants to send a username or a chat message that includes spaces. Textual data is typically less space efficient on the network and takes longer to read and write than binary data. Not necessarily an immediate concern, it again depends on the game and how often you need to send and the volume of data you are sending.


#5206725 Getting array boundary exception that is so weird

Posted by rip-off on 26 January 2015 - 11:20 AM

Probably needs a double-dereference, sorry. Iterators, when dereferenced, yield the container's value type - which is in this case a pointer. A second dereference is necessary to bind to a reference.




#5206689 Encapsulation through anonymous namespaces

Posted by rip-off on 26 January 2015 - 06:19 AM

The hard part is managing instantiation. To instantiate the object, C++ needs to know it's size. This means that in Oberon_Command's example, either the main.cpp file needs to be able to include a special header that gives the class definition, or you need to switch to dynamic allocation,e.g. the original header contains a factory function to return something like std::unique_ptr<Instance>.

 

That interface is very C like (opaque struct). Consider also the PIMPL idiom.






PARTNERS