why is c# better for game development than c++(you can be vague)

Started by
298 comments, last by normalme 19 years, 11 months ago
struct IDontWantToDie {    shared_ptr<IDontWantToDie> aliveAndKicking;    ~IDontWantToDie() { std::cout<<"AHHHH DIEING!!!"<<std::endl; }};int main(int argc,char** args) {    shared_ptr<IDontWantToDie> forever = new IDontWantToDie;    never->aliveAndKicking = forever;}


thats the smallest possible circular pattern. compile it. you won''t see the destructor. it won''t be called.


if memory management could be done that easily, believe me, it would be done that way. but the only generic way to solve memory management once and for all, is gc. and even gc has it''s problems.

it saves you ONLY from having "dangling pointers". it can''t really save you from memory leaks sometimes. problem is, if there is one valid reference, the object will stay.. this can sometimes get ugly interconnections that let nothing die in the end:D



If that''s not the help you''re after then you''re going to have to explain the problem better than what you have. - joanusdmentia

davepermen.net
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Advertisement
quote:Original post by teamonkey
quote:Original post by Anonymous Poster
Doesn''t any C# developed program presently require the client (person running the program) to have the .NET Framework installed presently?

that alone is it''s greatest shortcoming imo.


That will change. You only need to download it once, and it will be stuck on cover discs and distributed on CD with commercial apps that need it. And it''s an integral part of Longhorn.
[teamonkey]



WTF? Its on my XP cd!!!

die or be died...i think
quote:Original post by Fidelio66
It is much more powerful, it has all features of C++ and then some more.



That I laugh at. However, I''m probably one of the few programmers who actually uses multiple inheritance for something other than confusing my peers?

quote:Original post by AndyTX Sure, but this isn''t an argument for C# over C++. Almost all of the above (save the last really ) can be done automatically in C++ Builder for example, at the click of a mouse.

It is an argument for c# over c++. One language has a ton of hideous stupid things that are really just compensation for the bad linker technology in the late 1980s. The other language was designed after seeing the hideous things these original things would do to source code.

IDEs will NOT keep your headers and sources in sync with just the click of a mouse. And even with precompiled headers, the build time of any non-trivial c++ program dwarfs that of its c# equivalent.

quote:
Furthermore I can''t see how making windows programs can be easier than using the VCL, considering I can make a full user interface to, say, connect to the internet, download a file, ftp a file, send e-mail etc. without ever writing any significant code.

Go check out the .net forms designer.

quote:
All I''m trying to say is that while I accept C# as a reasonable language, most of the arguements being made are NOT about the language itself. Sure it''s better than Visual C++ for many of these things, but that''s more the fault of Visual C++ than the language. Anything that you can do in C# that you can also do just as easily in C++ Builder (for example... it tends to be the best and most full-featured compiler that I''ve found, albeit expensive for that reason) does NOT count as an advantage of C#, for obvious reasons.

I''m sorry, but you''re just wrong. Most of the arguments ARE made about the language itself.

Namely:
the c++ language requires that #include pastes in source code, because there may be different symbols defined in different translation units, which may affect compilation. This means that one header file will be parsed and compiled once for every source file it is transitively referenced in.

The need to keep headers and sources in sync, which means that changing an interface anywhere requires another change in a different file.

Lack of garbage collection which makes certain idioms problematic, and causes developers to have to waste time dealing with memory allocation and ownership issues, rather than focusing on the actual problem they are trying to solve. Note that this problem also creates unneeded confusion regarding pass-by-value and pass-by-reference, since now arguments will be passed by ref for performance reasons.

The lack of an enforced type system, which allows programmers to commit subtle, difficult to track down errors.

these are not compiler issues; these are language issues.
I think I see what you mean now. Because the reference count will be 2, forever won''t be destroyed, and the counter will simply be decremented to 1?

I can see how that would be a major pain, though I''ve generally tried to limit the use of smart pointers as much as possible and haven''t yet run into a situation like this.
Hey sjelkjd,

I think you make some valid points in defense of C#, and there are certainly some hideous things you can do in C++ (I know cause I've done some hideous things myself), but at the same time, there are some amazing things you can do in C++ that you simply can't do in C# (at least as of yet).

You mentioned the lack of an enforced type system. This generally applies only to the primitive types, and most C++ programmers use them simply as building tools for advanced types. This is certainly a disadvantage, but I don't think they cause many problems for experienced developers.

>> Lack of garbage collection which makes certain idioms problematic, and causes developers to have to waste time dealing with memory allocation and ownership issues, rather than focusing on the actual problem they are trying to solve.

This is true, but I would argue that C# can still require you to focus on a whole new set of issues as well, as davepermen pointed out. The inability to explicitly control the allocation and deallocation of an object requires you to track down every single reference if you want to avoid internal memory leaks, which I imagine can be quite challenging in a large scale system.

>> Note that this problem also creates unneeded confusion regarding pass-by-value and pass-by-reference, since now arguments will be passed by ref for performance reasons.

I don't think this creates any more confusion than in C#. The behavior is simply reversed. C#, by default, passes things by reference while C++ passes objects by value. A programmer should still think about whether he/she wishes to deep copy or shallow copy an object. In fact, if he/she doesn't know well enough to decide, I would think that it would be less confusing and potentially less problematic to pass and return copies around. It's worth mentioning that a problem with many C++ programs is that many programmers do a lot of unnecessary copying. However, I would still bet that many of them outperform similar C# programs. It's when a programmer shallow copies an object and hopes to be working with a copy when the bigger, more difficult problems arise.

Additionally, I think some of your points are really IDE-specific (though you're probably perfectly aware of it). While C++ IDEs at the present may have synchronization issues between headers and implementations (I avoid most fancy IDEs in C++ so I wouldn't know), that's still a feature of the IDEs and not C++. It doesn't mean it would be impossible for an IDE to be created that would keep the things in sync. The form designer is not part of the C# language. A person could just as well write a C# program in notepad and do all the code for the assemblies and the form-generating code themselves. Furthermore, I've discovered some synchronization issues myself while using the .NET form designer, such as events persisting even after I delete a control or form designer code persisting for events even after I delete the definitions of those events.

Also, you mentioned C++'s longer build times. This is certainly a disadvantage, but it's due to the fact that C++'s power comes mainly from its static, compile-time capabilities. This is why applications using templates as a means of generic design can greatly outperform C# applications which use object boxing. Templates slow down compile time but have no effect on the application's performance at runtime.

On a similar note, most of the commonly used C++ libraries are statically compiled and linked, including the STL (and for good reason). This contributes to a great portion of the build time. If you wanted, you could certainly take the functionality from these libraries and compile your own dynamic linked libraries and avoid the lengthy build times that way, as C# does. However, you would have to sacrifice the use of templates and use other techniques for generic programming which would inevitably lead to a reduction in efficiency. If C# had to statically compile and link the full .NET framework, the build times would be horrific and the executables would be huge.

From my standpoint, I can always make a C++ program compile faster at the cost of runtime performance or by writing a bunch of DLLs. There are also a lot of techniques such as pimpls to reduce compile-time dependencies. However, in C#, I have no choice but to do a lot of things dynamically which I could have done statically to get a more efficient program.

In terms of importance, however, I usually find that greater efficiency at runtime generally outweights the speed at which a program can be compiled and linked. A lot of people say that computers these days have so much memory and so much speed that programmers need not worry about such things, but there are still a number of applications where performance is crucial. Server-side applications, for instance, often need optimal performance to serve as many people as possible with a given set of hardware.

I think one of the greatest advantages of C#(aside from better type checking, ease of creating DLLs, etc.) that people haven't mentioned too much is in uniformity. The language restrictions can lead to a greater uniformity in design, which allows for programmers to work with other people's code more easily. Further, the fact that the whole .net framework library is designed by Microsoft provides a level of uniformity. You generally don't have to worry about standardization issues and compatibility problems when software is being developed completely by a single company. People who want to do threading, sockets, graphics, sound, etc. in a C++ application generally turn to an assortment of libraries which can vary greatly in their design and workflow.

However, just taking out features from a language because a lot of people misuse them is not always the best solution, in my opinion. This is the big problem I see in C# and Java's approach to 'better design'. It might make the general population design better, but it restricts the more advanced people from doing a lot of good stuff.

I was formerly a visual arts major, and I've seen a lot of artists have a hard time getting used to oil paints and watercolor. With oil paints they end up over-blending and getting muddy results. With watercolor they try to layer too much or use too much water and end up with muddy results that bleed all over the place. This doesn't mean that the ideal solution is to just take oil paints and watercolors away permanently and let all artists paint with gouache or digital paints instead. While a lot of beginning artists may do some really ugly stuff with those paints, there are some really gorgeous oil paintings and watercolors out there.

- David Ikeda

[edited by - nyugfu on February 19, 2004 1:45:34 AM]
We just need a new language that extends from c++ and have similar syntax as c#. We can write a "new language" parser that translates code to c++, it can also insert a small GC into our projects too
What''s this, page 15? And it hasn''t been locked yet?
quote:Original post by nyugfu
I think I see what you mean now. Because the reference count will be 2, forever won''t be destroyed, and the counter will simply be decremented to 1?

I can see how that would be a major pain, though I''ve generally tried to limit the use of smart pointers as much as possible and haven''t yet run into a situation like this.


this is it exactly.

it is no general solution. there isn''t any.

except gc solutions.



If that''s not the help you''re after then you''re going to have to explain the problem better than what you have. - joanusdmentia

davepermen.net
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Hey Dave, have you ever run into a situation though where you needed complex interdependencies between smart pointers to a point where you were making circular references without being aware of it?

I've always used them mainly to avoid having to write destructors and also as temporaries in functions so that if an exception it thrown or some other condition causes me to leave the function abruptly, I won't have to remember to deallocate it. While I haven't written any truly large scale systems yet, I have used C++ for a good 6 years (and very frequently on a dialy basis as well) and I've written a few online games that spanned a little over 200k lines of code for all the different editors, servers, and client code and I've never had a need for smart pointers to an extent where I ran into any serious problems like this.

On a side note, I hated C++ for quite a while before I began to appreciate some of its finer features. I was far more into Pascal, BASIC(bleh) and Java when I first started using it and thought C++ code was just plain ugly and unnecessarily complex. I felt I could write better applications faster in other languages, though now I feel the opposite. I now feel like I can write many applications better and faster in C++ than in C# or Java, though there are still applications where I prefer Java or C# instead - especially ones with complex GUIs. Nevertheless, this is because of the libraries that come with C# and Java more so than the true language features themselves. If I omit the libraries and focus only on the language features, however, I think I can almost undoubtedly write more efficient applications with less code that's also more manageable and more reusable in most situations using C++.

Getting back to the point, I think circular references and other issues can be completely avoided, however, to a point where you don't even have to worry about them.

There was a time when I wanted to use a smart pointer to enable me to work with multiple, modifyable instances of a single point/polygon (I was working on a geometry plugin for Lightwave). I wanted the memory for a point or polygon to be deallocated when all instances were no longer in use. Containing all the point and polygon object instances in reference-counted smart pointers seemed like the way to go, but upon thinking further, I found a much easier solution by making the internal members of the point and polygon classes contained in a smart pointer. Simplified example:

class Point
{
private:
shared_ptr<SomeType> data;
/* ... */
};

Now I could just essentially shallow copy the internal data of the point without having to deal with the smart pointer in the client:

Point p1(some constructor arguments);
Point p2 = p1;

As opposed to doing this:

class Point
{
private:
SomeType data;
};

shared_ptr<Point> p1 = new Point(some constructor arguments);
shared_Ptr<Point> p2 = p1;

Looking back at the first example, I did not need to ever copy points, only allow multiple instances to the same one, but if I needed to, I could have made an explicit clone method like the ones provided in the object class in .NET that makes a deep copy.

The first example made it so that the actual point and polygon instances(as visible from the client's perspective) did not have to be allocated dynamically. They could be copied, passed, and returned all over the place by value, yet they would naturally just be shallow copied into multiple instances of the same data(because the internal members of the class were managed by a smart pointer).

Therefore, the smart pointer was encapsulated in the class, and I was still able to have multiple objects of a Point by copying/passing/returning by value. The problem with the second version is that it becomes the responsibility of the client code outside the class to handle the shallow copying, and I imagine it's when this happens that you end up getting smart pointers to objects which contain smart pointers which contain smart pointers and so forth.

The fact that the smart pointers weren't exposed in the first example allowed me to avoid having to worry about complex things like making smart pointers that point to smart pointers and the client didn't have to worry about using smart pointers either.

Simiarly, if objects of Point were needed for another class, I wouldn't have to worry about having to use smart pointers in that class, and I imagine the problem of circular references will be virtually wiped out this way.

By using solutions like this to make memory management contained fully inside a class and avoiding the need to deal with memory management issues outside the class, I don't think you would ever run into a stumbling block or have to worry about the possibility of circular references or any other difficulties.

Take the example you showed me, for instance, of how to prevent the destructor from being called through a circular reference. While I know the example was meant to just be a simple thing to demonstrate how this would happen (and I'm sure the real situations the problem occurs in are extremely more complex), the problem simply wouldn't exist if you avoided making instances of the class containing the data managed by a smart pointer and instead made only the primitive data at the lowest level of the hierarchy managed by a smart pointer (and nothing else). When you do this, you never have to worry about the destructor of an object not being called because dynamic allocation and smart pointers simply won't be involved at all when creating instances of these objects.

Also, you probably knew this and just wanted to illustrate the possibility of it happening, but in your example, it simply wouldn't make sense to make a smart pointer of IDontWantToDie since its member is already contained in a smart pointer. There's no need to make a smart pointer to something in which its data is already shallow copied and reference-counted in the first place.

Still, I acknowledge the fact that it is a problem, though I think there are always ways to work around it or even avoid it completely in the first place. I do think C#'s GC method is handy, but I don't think it or any other features in C# make it clearly better because C# is missing a few really great features that are offered in C++. I think both languages can be equally matched considering their various strengths and weaknesses.

I think the two biggest things missing in C# that I miss from C++ are:

1) A means of statically generating similar code with only small vartiations(i.e, templates)
2) The ability to mix procedural programming with object-oriented programming. Some may view this as a bad thing, but I think the C++ STL demonstrates how the two can work in harmony.

There are also a number of smaller things like multiple inheritance.

Like you said earlier, I also like to manage stuff myself sometimes where I know the lifetime and ownership. In fact, the majority of the time I want to be able to do this. I think the ideal solution would be to have the option of using GC without being required to. C# could just provide it as another type of smart pointer that has built-in language support for its garbage collection methods or work like managed C++ and let you use keywords to specify whether a certain class has automated garbage collection or not.

- David Ikeda

[edited by - nyugfu on February 19, 2004 9:21:07 PM]

This topic is closed to new replies.

Advertisement