Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

HB posted:

The first code copies the string constant into the variable-sized array a, the second creates a pointer to the string constant before attempting to change it. These compiled-in constants are read-only by default; if you're using GCC there's a flag you can use to allow writing to them.

This may just have been a slip on your part, but for new programmers it's important to note that the array a created by

code:
char a[] = "hello world";
is not variable-sized. Its size is implicit, but still static and determined at compile-time. The compiler will read the length of the string you are initializing it with (in this case 12), and convert it to

code:
char a[12] = "hello world";
before compiling.

Adbot
ADBOT LOVES YOU

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?
Being aware that invoking reinterpret_cast is something that should be done with extreme care in good C++ code, I was wondering if there is anything wrong with this, or if there is a better way to do this:

code:

struct Packet {
  uint16_t foo;
  uint16_t bar;
  uint8_t isLong;
  char optData[];
};

struct LongPacket {
  Packet shortPacket;
  uint16_t extraField1;
  uint16_t extraField2;
};

void parsePacket(Packet* pkt) {
  std::cout << "Foo: " << pkt->foo << std::endl;
  std::cout << "Bar: " << pkt->bar << std::endl;
  std::cout << "Is Long: " << pkt->isLong << std::endl;

  if (pkt->isLong != 0) {
    LongPacket* l_pkt = reinterpret_cast<LongPacket*>(pkt);
    std::cout << "Extra Field 1: " << l_pkt->extraField1 << std::endl;
    std::cout << "Extra Field 2: " << l_pkt->extraField2 << std::endl;
  }
}
Where this packet is something that is going to be sent/received over a network socket, and assuming, of course, that the isLong field is accurate.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

JoeNotCharles posted:

Since this is C++ code, why doesn't LongPacket just inherit from Packet so you can use static_cast?

I considered that, but - and I guess this should have been an alternate question - I couldn't find any definitive answer on whether or not that would preserve field ordering and packing.

Since it's a network packet, it needs to be bit-for-bit accurate. So, if I write:

code:
#pragma pack(1)

struct Packet { 
  uint16_t foo; 
  uint16_t bar; 
  uint8_t isLong; 
}; 

struct LongPacket : public ShortPacket { 
  uint16_t extraField1; 
  uint16_t extraField2; 
};
Am I absolutely guaranteed that if I copy the contents of a LongPacket struct into a char[] buffer that, for example, buf[56] is the first byte of extraField2?

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Vanadium posted:

I do not think the language guarantees absence of padding in any case, with or without inheritance.

In gcc/g++, the #pragma pack(1) directive guarantees the absence of padding in plain structs. However I'm looking but I can't find out if this also applies if you use a struct more like a class (for example, adding constructors, using inheritence, etc).

I'm not terribly concerned about portability to other compilers at the moment, but this will have to build on gcc for several platforms, so I don't want to rely on something that might work on x86 but not on ARM.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?
This may be silly/stupid, but is it in any way possible to do this?

code:
class Foo : public Bar<Foo::Things::Thing> {

public:

  struct Things {
    enum Thing {
      // ...
    };
   };

   // ...

};
Basically I have an enum declared (publicly) within a class. The enum is contained by an otherwise empty struct for the purpose of giving the enumerated values their own namespace. I want to pass the enum type as a template argument to a class that I want to inherit from. I can't seem to find any way to forward-declare things that occur inside a top-level class, and I'm thinking this might be impossible simply because I'm asking a base class to do something with a type declared in a derived class, but I'm wondering if there is any way to get this effect.

The motivation behind this is that I'm writing a number of classes that all have several identical member functions that accept enumerated values, but the enumerated values are different for each class. I was thinking it would be handy to have a common templated base class that handled the implementation of these member functions so that I wouldn't have to duplicate the code. The other way to do this (my present solution) is to have each class call templated utility functions that are in an unrelated clsas, but in that case there is still an identical interface to the utility function in each class.

Obviously one solution is to move the Things struct outside of the class, but then the namespace for the Thing values would have to be FooThings:: instead of Foo::Things:: which seems inelegant to me.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Mikey-San posted:

The strcmp() function isn't "does this match or not"; it's not a yes-or-no operation. Rather, strcmp() is a lexicographical comparison function that determines the ordering of two strings which can be identical, sorted A>B, or A<B. As a result (no pun), the return value of strcmp() is a signed integer that is equal to, greater than, or less than 0. You should perform strcmp() checks like this:

code:
if (strcmp(stringA, stringB) == 0)
{
...
}
At least, this is how I understand strcmp() to work. If anyone knows better, please tell me. :)

You're correct, but using !strcmp(A,B) as an equality test is a fairly common idiom. It works of course because anything nonzero is true, and strcmp(A,B) evaluates to false (0) if and only if A and B are the same.

It's a non-intuitive and inelegant syntax, but if you've been coding for any length of time you should be familiar with what it means, so there's nothing terribly wrong with using it unless you're trying to teach someone else.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

clockwork automaton posted:

I'm working on creating a multi-threaded telnet server, eventually in C++, but for now I'm focusing on C just trying to get it working and ignoring threads at this point. However, I'm having issues binding to port 23.

Is your program running as root? Ports below 1024 are reserved for superuser use only.

Edit: By the way, the C socket functions all set errno, so you can use perror to find out in more detail what the exact error was by replacing your printf call with a call to perror (and possibly including errno.h).

Smackbilly fucked around with this message at 00:06 on May 13, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Zaxxon posted:

int *Foo

this reads to me as someone declaring an int named *Foo. Why don't they write

int* Foo

this reads to me as someone declaring an int* named Foo.

In the first case, think of * as the derefernce operator. Think of int *foo as "I am declaring that there shall be an int at the location pointed to by foo", and int* foo as "I am declaring foo as type pointer-to-int."

Obviously there's no actual difference between them, but that should help allay the irritation in seeing int *foo.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?
pthreads question:

Is there any disadvantage to creating lots and lots of threads over the course of a program's execution, if only a couple will exist at any given time?

The context is that I'm writing an app that does asynchronous communication over UDP. Since UDP is unreliable, certain packets that are not ACK'ed after a timeout need to be re-sent. The way I'm currently handling this is:

- send the packet
- spawn a thread which sleeps for the timeout interval
- when the thread wakes, it checks if the ACK has been received yet, if not it calls a timeout handler, which repeats this process.

So every packet that might need to be re-sent gets its own timeout monitor thread. There are only going to be a few (like, 1-5) of these packets pending at any given time, but the program may run for a very long time, so there could easily be hundreds or thousands of short-lived threads created over the course of the program.

Is there any problem with doing this, as long as I make sure to either detatch or join each thread?

I could certainly consolidate timeout handling into one thread, but that would make the code messier and more complicated, so I'm inclined to avoid that unless there is a good reason to do it.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Zombywuf posted:

Why are you emulating TCP?

1. I didn't write the protocol
2. Not all packets are ACK'ed, only certain important packets
3. I'm not emulating TCP connections or flow control

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Insurrectum posted:

When should you use cout as opposed to printf? And scanf as opposed to cin?

Always and never, respectively. There's no reason to use C I/O functions in C++ when there are C++ I/O functions that do everything the C functions do.

quote:

When you use std::function is that just calling the function in std without having to put "using namespace std;" at the top?

Yes. The :: is the scope-resolution operator. You know how when you declare a local variable in a function that variable is not accesible outside the function, so you can re-use the name in other places? Namespaces work kind of the same way, except that you can explicitly refer to a variable inside a namespace from outside that namespace by using namespace::variable. For example, cout is a variable inside the std namespace.

"using namespace std;" means "import ALL of the variable names from the std namespace into the current namespace". You can of course refer to any variable in the current namespace without ::, so this allows you to write cout instead of std::cout. (Note that if you have not declared a namespace, you are working in a default unnamed namespace, which is usually fine)

In some respects "using namespace" is poor style because you're polluting your current namespace with lots of variables that you may not really need. You can be more restrictive by writing "using std::cout;" and "using std::endl;", etc, to import specific variables from another namespace.

If you are familiar with Java, "using namespace foo" is like "import foo.*" and "using foo::bar" is like "import foo.bar". However unlike Java, namespaces are not necessarily associated with a class (although every class is also a namespace).

Smackbilly fucked around with this message at 00:44 on May 30, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

ehnus posted:

One big reason to avoid C++ I/O functions is that they add an implicit dependency on exception handling mechanisms which isn't always an acceptable option.

Okay but if you're eschewing all forms of exceptions, then you're also tossing out STL and basically working in C-with-classes. I suppose some people do that, though.

quote:

In practice there's nothing wrong with "using namespace ...", in fact as someone who used to work in QA I greatly encourage people to use it because seeing fully qualified names littered throughout code is ugly as sin and in an individual translation unit there's no real danger regarding namespace pollution. The exception to this is in public header files -- people who import namespaces in public headers need to be shot.

I wasn't advocating using fully qualified names, I was advocating the middle ground of using "using" statements that specify exactly which symbols you wish to import.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

SkankerX posted:

It's nice to put that sort of stuff in an enum, since it's a bit cleaner looking and conceptually makes sense. That may just be my stylistic preference, though.

Tbe biggest advantage is that enums are typesafe, so you will not need that extra "assert" statement to check that the parameter is in the valid range. If you have:

code:
enum Directions { North = 0, South, East, West };

void Room::link(Room* dest, Direction dir) {
  links[dir] = dest;
}
The compiler will automatically complain if you try to pass anything other than one of the valid values as the second argument.

Also note that there is no problem with using an enum value to index an array - enum values are automatically promoted to integers. Note that I have assigned North = 0 to ensure that the numbers assigned to the enum values begin from zero (since I'm not sure if that's mandated by the standard or not).

But you will still need a constant recording how many valid values there are for the purpose of declaring the links[] array. Unfortunately there is no way to simply query an enum type and ask it how many valid values there are.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Vanadium posted:

room->link(0, Direction(14));

:(

Well there what you're doing is performing a type-unsafe C-style cast. If you go around doing that, all the type-safety in the world isn't going to help you, since you're basically telling the compiler "I know better than you do here; shut up". And if you actually don't know better...

By the way, regarding the "counting the valid enums" problem, here's how I usually handle it:

code:
struct Directions {
  enum Direction { North, South, East, West };
  const unsigned int NUMDIRECTIONS = 4;
};
This has the effect of keeping both the enumerated values and the counter constant inside their own namespace, so you refer to "Directions::North" instead of just "North", which allows you to use the symbol "North" elsewhere without conflict. Plus, if you're consistent about naming, you'll know that the type Foos::Foo will always have an associated Foos::NUMFOOS constant that you can use.

Smackbilly fucked around with this message at 13:04 on Jun 5, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

floWenoL posted:

Mustach posted:

Why do you use a struct declaration instead of a namespace?

Because not everyone programs in C++.

Actually the real reason is because you can't declare a namespace inside a class. So if I want to do something like this:

code:
class Foo {
public:
  struct States {
    enum State { Initiailzed, InProgress, Aborted, Completed };
    static const unsigned int NUMSTATES = 3;
  };
};
To get the effect of having a Foo::States namespace, the only option is to use a struct (or a class, but why have an extra "public:" line?).

Smackbilly fucked around with this message at 16:26 on Jun 6, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?
This is more a gcc question, but:

Is there a way to force gcc to link in everything from a static library, even if you're not using it?

The context is that I want to create a shared library libfoo.so which uses libbar.a, and I want EVERYTHING in libbar.a to be available to programs which link against libfoo.so without having to have libbar available.

The problem is that there can be programs which use symbols in libbar.a which are not used by libfoo.so, so by default these symbols are not included in libfoo.so when it statically links against libbar.a.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Shoes posted:

What am I doing wrong?

code:
    char *request = "GET [url]http://www.google.com/[/url] HTTP/1.1 \r\n";

HTTP requests need to end with a blank line (because there can be multiple lines in a single request). Stick another \r\n on the end and you should be fine.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

TheBlackRoija posted:

when I try to compile it a get a whole bunch of "'WORDS' undeclared (first use in this function)" errors

Please post the exact error messages.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

more falafel please posted:

The biggest problem people generally have with learning pointers is that they're told "oh yeah, pointers are hard, that's why I hate C," and then they believe it's some horrible convoluted thing.

Agreed. Pointers are hard to avoid making mistakes with (memory leaks, etc). They aren't hard to understand. A lot of new programmers don't pick up on that distinction when some people bitch about pointer problems.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

chutwig posted:

That's great, except notwithstanding that I forgot printf's return type due to my inexperience, it still doesn't explain why saying
code:
printf("%s\n", curr_q->current_query->str)
would crash in the code but not in the debugger. I'm not sure why you're telling me printing curr_q isn't relevant, because at no point do I actually print it n the code. print looks like printf, but is the same command as inspect in gdb.

My first thought with this bug would be that the string that you're printing is not null terminated. In GDB the block that you malloc might be set to all nulls, whereas at runtime it may not be.

Try using memset to set your string to all NULs right after you malloc it, and see if the problem goes away. If so, double check your sizes and string copy operations to see where you're writing one too many characters into the string.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

floWenoL posted:

What do you guys think of this?

The first thing is that it shouldn't be used as an all-purpose C++ coding guide. For instance, Google C++ code does not use exceptions, ever. This makes sense in the context of the performance requirements that Google's production code has, but there are plenty of instances where using exceptions is the better choice for purposes of legibility and maintainability.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Hammerite posted:

Thanks.

I have another question. I need to be able to concatenate two strings, but none of the methods I find online seem to work.

...

How do I change the above line so that it will work? I find it hard to work out what type of string data type I should be using / how to get it (LPSTR, CHAR*, char[36], const char, the list seems to go on!)

The + operator does not concatenate C-style strings (i.e. character arrays). You need to use the strncat function. And if you're in C++ and not C, you should probably use the std::string class instead of character arrays.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Hammerite posted:

I haven't any problems at the moment, although it was hard to get the program to clear a string (so that it's blank, or appears blank) because except when initialising the string, it complains that the character array "" and the array I'm assigning it to aren't the same length. The workaround I found is to tell it the first element of the string is the terminating character, i.e.

code:
line1msg[0]=*""
As you can probably tell, I still don't really understand the way strings work.

Like the last two people have said, #include <string> and use std::string, it's much easier almost all of the time. However, it is worth you while to learn how C-style strings work, since you will encounter them:

A C-style string is an array of characters which is terminated by a special character called NUL. This has ASCII value 0, and is represented as '\0'. Anything after the NUL character in a C-string is ignored. This means that the 6-character array 'H' 'e' 'l' 'l' 'o' '\0' and the 10-character array 'H' 'e' 'l' 'l' 'o' '\0' 'x' 'y' 'z' '!' are both considered to be the same string "Hello". If a C-string lacks a terminating NUL, you've got problems because there's no way to find out how big an array is at run-time, so functions expecting a C-string won't know when to stop reading from the array.

Consequently, if you have some string char mycstr[10], to "clear it", it is sufficient to write mycstr[0] = '\0'. Note that when you write an initializer like char mycstr[10] = "Hello", the compiler will automatically add in the trailing \0, so long as your array is large enough to hold it. Note also that this means that an array which holds a C-string must always be at least one character larger than the number of printable characters that it intends to store.

The key thing to remember about C-strings is that they are really only character arrays with this agreed-upon convention that they terminate with '\0'. The language does not treat them any differently than other arrays, and consequently operators like = and + don't do special string assignment and concatenation operations like you might want them to. For example, you can't do this:

code:
int intArrA[5] = {1, 2, 3, 4, 5};
int intArrB[7] = {10, 20, 30, 40, 50, 60, 70};

intArrA = intArrB; // doesn't work!
so you also can't do this:

code:
char charArrA[5] = "Bill";
char charArrB[7] = "Ernest";

charArrA = charArrB; // doesn't work either!

charArrA = "Eric"; // also doesn't work 
// ^^^ This kind of syntax only works in initializations. 
Therefore, the C standard library has functions like strncat (concatenate strings) and strncpy (copy a string into another array) and strlen (how many characters are before the \0 character?). The way these functions are implemented is dead simple. strncpy, for example, takes three parameters, a destination array, a source array, and a maximum number of characters to copy. All it does is go through a while loop that copies each character from source to destination until either it copies a '\0' character or it copies the maximum number of characters. No special string magic involved.

But the thing to be careful about with all of this is making sure that you arrays have the appropriate size. Arrays in C do not automatically grow to accomodate new data. If you try to write 10 characters into a 5-character array, your program will crash if you're lucky or have other "interesting" and tough to find bugs if you're not.

This means that if you do not know at compile-time how long a string is going to be (user input, for example), you need to dynamically allocate memory for the character array. And then you have to make sure you keep track of that memory and free it later to avoid a memory since C/C++ have no garbage collector.

Oh, and then there's the interesting part where you will see C-strings referred to as char* (character pointers) instead of character arrays. In most cases in C, an array is interchangable with a pointer to its first element. So it's perfectly legal to have a character pointer char* foo, and say foo[5] = 'x';. This is of course assuming that you know for sure that foo is size at least 5, which you can't determine at run-time, which is why we have this whole business about '\0' characters earlier.

Phew. And that's really just the half of it.

In short, C strings are a huge pain in the rear end and are ridiculously error prone. Even many professional commercial programs have bugs and security holes because some programmer forgot to be careful when dealing with arrays (usually character array). You should be aware of how they work because you will encounter them, but if you are fortunate enough to be programming in C++, there's no reason to subject yourself to one of C's biggest annoyances. That's why God (Bjarne Stroustrup) invented std::string.

std::string works like you would expect a string to work. = copies/assigns, + concatenates, it has a .size() method to find out how long it is, and it grows and shrinks as necessary with no need for manual memory management. It even has a .c_str() method which will convert it to a C-string if you encounter a function that doesn't accept std::string. It really is worth it to learn how that works.

C++ even has other classes (e.g. std::vector) with which you can usually entirely avoid using C-style arrays in a C++ program. As you learn more about C and C++'s memory management, pointers and arrays will make more sense to you and you'll 'get' why C-style arrays and strings are the way they are, but even when you do understand it, there's no reason to subject yourself to all the inconvenience of working with them when there's a perfectly good and intuitive C++-style way to address the issue.

Smackbilly fucked around with this message at 15:39 on Jul 2, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Entheogen posted:

oh ok, i see what you are saying. even though macro is replaced by pre-compiler with whatever it is defined to, if data is a function call it will be evaluated 15 times.

Yes, and what's worse, if that function call has any side-effects, those side-effects will be repeated 15 times.

Imagine what would happen if f() were instead randomGenerator.nextNumber().

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Triple Tech posted:

Question for the peanut gallery. How do you select a random integer from 0 to something higher than RAND_MAX? (Which I'm assuming is static) I'm guessing we're getting into algorithm territory...

I come from a world where random numbers are floating in (0,1).

If RAND_MAX is a power of two minus one (which it usually is), you can generate two random integers, and consider them to be two halves of an integer with twice the number of bits set in RAND_MAX. This can be done with n random integers representing a number with n times the number of bits in RAND_MAX.

For example, in the GNU C library where RAND_MAX is the largest representable int32_t (231-1, so 31 bits set), you generate two ints with rand(), and then OR them together to build a 62-bit integer which is equally random. (...assuming of course that you have or can create a datatype that can hold a 62-bit integer)

If RAND_MAX is not 2x-1, then this isn't uniform, but there's probably a way around that if you're really interested in that corner case.

Edit: It seems like you know this, but for lurkers in this thread, things that definitely do not work include...

1. Generate two numbers and add them together. Nope, the distribution will be biased in favor of numbers towards the middle of the range. In order to hit the extremes, both numbers must be either large or small simultaneously, which is less likely than other combinations.

2. Generate two numbers and multiply them together. Nope, (among other problems) the distribution will be biased towards even numbers. The result number will be even if either of the original numbers were, making the result three times more likely to be even than odd.

The reason that the OR'ing method works is that if the original PRNG is any good, every bit in the original 31-bit numbers is just as random as every other bit, so if we generate 62 totally random bits, they're still totally random no matter what order we put them in, as long as we don't change them in any way (like by doing math with them).

Smackbilly fucked around with this message at 18:37 on Jul 7, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

bcrules82 posted:

can rand() be called within a const method? anyways, i'm just returning a private variable.

A const member method only guarantees that the internal state of the object will not change (and even then there are legitimate reasons that the method might internally cast away constness for some operation). It does not at all preclude stateful library calls.

In your case, yes, there is no logic-related problem with calling the accessor 15 times. There might be a performance hit if you are using this macro in a loop that runs many times because your compiler may choose not to inline the accessor for some reason.

However the main thing we're pointing out is that one reason to avoid macros generally is because they are prone to bugs like this where the behavior of a poorly written "function macro" is different than the behavior of an equivalent function. Ideally, you shouldn't have to care if EVEN_BITS_W is a macro or a function call, because it should work the same way. In the case of the particular EVEN_BITS_W macro that was posted, this assumption is violated because the macro version re-evaluates the argument 15 times, whereas if it were written as a function, the argument would be guaranteed to only evaluate once. This means that if you ever later decide to re-use EVEN_BITS_W with an argument that is not a simple accessor, you do have to stop and carefully think about whether the argument can handle being evaluated 15 times with no ill effects.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

UberJumper posted:

3. Why should i ever use pointers? The only reason i can see them being useful, is for when creating and allocating big blocks of memory, then having that memory freed half way through the application. I've always passed everything by reference, returned references when i can etc.

Consider the scenario in which a class Foo has a member that needs to refer to some other object of type Bar. Say that either you don't know what Bar object you need to refer to when you construct your Foo object, or that you want to be able to change which Bar object the Foo uses at will.

Given that references cannot be uninitialized or null, and they cannot be changed to refer to something else, how do you do this with references?

Answer: You can't; you use pointers.

References in C++ are basically just shorthand for pointers when you only need to do very basic things with them. When you need to do more complex things like have a pointer that points nowhere, or a pointer that changes where it points to, or a pointer that points to a dynamically sized piece of memory, or a pointer that doesn't point to the beginning of an allocation, then references don't support what you want, and you have forgo the shorthand and use pointers directly.

The benefit of references of course is that because they disallow those things, and those things are the kinds of things that end up causing pointer-related errors, references are less error-prone, and so you should use them when you can. But their relative safety makes them very limited, and when you start writing larger C++ applications you will encounter these limitations frequently.

Smackbilly fucked around with this message at 20:26 on Jul 20, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Entheogen posted:

I used pointers before to switch between two arrays that contain same types.

such as
code:
vector< int > A, B;
vector<int> * curr = & A;
while( true )
{
   // do something 
   if( curr == & A ) curr = &B;
   else curr = &A;
}
Is there a better way of doing this without pointers? Or is this fine. Also in this case the pointer, is of course, pointing to something on a stack, right?

Yes, curr is pointing to the stack. As far as whether there's a more elegant way to do that depends on what exactly you are trying to accomplish.

quote:

If I did something like this:
code:
int * a = & int(5);
then a is pointing to something on the stack and will be gone after it is popped off?

This isn't legal C++ (does your compiler allow it?). int(5) is nothing more than the integer literal '5' being C-style casted to an integer, which does nothing. Literals are not lvalues (things that can appear on the left side of an = assignment) and therefore you can't legally take their address because they might not have one.

quote:

Also is there a way to get around using some confusing rear end referencing and dereferncing operators when dealing with iterators on containers that contain pointers themselves?

such as
code:
#define FOREACH(_it,_l) for(__typeof((_l).begin()) _it=((_l).begin());(_it)!=(_l).end();(_it)++) 

.....

class Foo
{
   public:
      void lol(){}
};

.......

vector< Foo * > lolz;
FOREACH( el, lolz )
{
   (*el)->lol();
}

I can't seem to think of a general way to get rid of that syntax with a macro, but that's not particularly confusing to anyone who has worked with STL iterators.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Brackhar posted:

So in preparation for job searching I've been systematically solving the problems posted at this blog as practice, but I'm having a bit of trouble figuring out a decent implementation for one of the questions that would be feasible in a timed environment. Here is the question:


I'm trying to figure out a solution that would work in strict Ansi C, but I'm not able to figure out an elegant approach. Particularly, my implementations seem to break down as they attempt to generate functions of the form ((1 + 2) – (3 * 4)). Can any of you provide some advice?

((1 + 2) – (3 * 4)) is a valid insertion of parenthesis. That example leaves off several valid insertions such as the one you had and, for example, ((1 + 2 - 3) * 4). However note that the question is only looking for the unique solutions that can be obtained through parenthesis insertion, not an enumeration of all possible parenthesis insertions.

If you want a hint as to how you should approach this problem, I'll tell you that this problem appears to be NP-Complete (read: don't try to brute-force it!), but it is a prime candidate for a dynamic programming solution.

There's nothing really C++ or C-centric about this problem - it's a test of your ability to design an algorithm.

Smackbilly fucked around with this message at 21:52 on Jul 20, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Brackhar posted:

So that seems the fairly obvious way to approach it, but what's the underlying mechanism to handle the evaluations? Do you parse through the string and create a tree, do you use a stack, modify the string in place...? I thought about constructing a tree, but I worried that in a timed situation I'd not be able to do the insertions in the proper order to generate all possible combinations.

Iterating over every permutation is an O(n!) brute-force algorithm. It will work, but it will have an insanely long runtime for anything but the smallest inputs.

I'm writing a solution to this problem because I'm bored and it's interesting, and here's the dynamic programming setup that I came up with:

Parse the input set so that V(n) is the value of the nth integer, and OP(n) is the operator appearing after the nth integer. Let N be the number of integers in the equation. Now define a function Q such that:

Q(n,n) = V(n)
Q(n,m) = { All unique values you can get by parenthesizing terms n through m }

And then your solution is the value of Q(1,N). The remaining step in designing the algorithm is how do you compute Q(n,m) for an arbitrary m in terms of V, OP, and Q? After you figure that out, you're down to the implementation details of what data structures you use to store the value sof Q, V, and OP in a program.

I haven't done a full complexity analysis on this, but I'm fairly sure this ends up being pseudo-polynomial (polynomial in the size of the largest integer value that you can generate in a subterm). So it should be fast as long as the integers involved are small, even if there are a whole lot of terms.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

sarehu posted:

Ultimately somebody can always throw you one of these:

code:
0 - 0 + 1 - 0 + 3 - 0 + 27 - 0 + 81 - 0 + 243 - 0 + 729 - 0 + 2187
and there's nothing you can do about it.


code:
tyler@kusari ~ $ time ./parse
Enter unparenthesized equation: 0 - 0 + 1 - 0 + 3 - 0 + 27 - 0 + 81 - 0 + 243 - 0 + 729 - 0 + 2187
Unique results: -3271, -3269, -3265, -3263, -3217, -3215, -3211, -3209, -3109, -3107, -3103, -3101, 
-3055, -3053, -3049, -3047, -2785, -2783, -2779, -2777, -2731, -2729, -2725, -2723, -2623, -2621, 
-2617, -2615, -2569, -2567, -2563, -2561, -1813, -1811, -1807, -1805, -1759, -1757, -1753, -1751, 
-1651, -1649, -1645, -1643, -1597, -1595, -1591, -1589, -1327, -1325, -1321, -1319, -1273, -1271, 
-1267, -1265, -1165, -1163, -1159, -1157, -1111, -1109, -1105, -1103, 1103, 1105, 1109, 1111, 1157,
 1159, 1163, 1165, 1265, 1267, 1271, 1273, 1319, 1321, 1325, 1327, 1589, 1591, 1595, 1597, 1643, 
1645, 1649, 1651, 1751, 1753, 1757, 1759, 1805, 1807, 1811, 1813, 2561, 2563, 2567, 2569, 2615, 
2617, 2621, 2623, 2723, 2725, 2729, 2731, 2777, 2779, 2783, 2785, 3047, 3049, 3053, 3055, 3101, 
3103, 3107, 3109, 3209, 3211, 3215, 3217, 3263, 3265, 3269, 3271

real    0m6.004s
user    0m0.036s
sys     0m0.000s
:D

Anyway, I'll put the solution code right in here because it's interesting/useful anyway and it's apparently not homework though it does make a really good dynamic programming exercise)

This is in C++. I know you were asking about ANSI C but I really didn't want to have to rewrite your standard map and set data structures when that's not really the point of this problem

code:
#include <iostream>
#include <vector>
#include <map>
#include <set>

using namespace std;

enum Operation { Add, Subtract, Multiply, Divide };

int doOperation(int x, Operation op, int y) {
  switch(op) {
  case Add:
    return x + y;
  case Subtract:
    return x - y;
  case Multiply:
    return x * y;
  case Divide:
    return x / y;
  }
}

set<int> Q(uint n, uint m, map<uint, Operation> OP, map<uint, int> V) {
  static map<pair<uint,uint>, set<int> > cache;
  if (cache.find(make_pair(n,m)) != cache.end()) {
    return cache[make_pair(n,m)];
  }
  
  set<int> unique_results;

  if (n == m) {
    unique_results.insert(V[n]);
  } else {
    for (uint y = n; y < m; ++y) {
      set<int> results_n_to_y = Q(n,y,OP,V);
      set<int> results_yplus1_to_m = Q(y+1,m,OP,V);

      for (set<int>::iterator i = results_n_to_y.begin();
           i != results_n_to_y.end(); ++i)
        {
          for (set<int>::iterator j = results_yplus1_to_m.begin();
           j != results_yplus1_to_m.end(); ++j)
            {
              unique_results.insert(doOperation(*i, OP[y], *j));
            }
        }
    }
  }

  cache.insert(make_pair(make_pair(n,m), unique_results));
  return unique_results;
}

int main()
{
  string input;
  map<uint, Operation> OP;
  map<uint, int> V;
  set<int> results;

  cout << "Enter unparenthesized equation: ";
  getline(cin, input);

  string::size_type nextNonWS = input.find_first_not_of(" ", 0);
  string::size_type nextWS = 0;
  int n = 1;
  bool expectNumber = true;
  string token;
  
  while(nextNonWS != string::npos) {
    nextWS = input.find_first_of(' ', nextNonWS);
    token = input.substr(nextNonWS, nextWS - nextNonWS);
    if (expectNumber) {
      V.insert(make_pair(n, atoi(token.c_str())));
    } else {
      if (token.size() > 1) {
        cerr << "Multi-character operator!" << endl;
        return -1;
      }
      Operation op;
      switch (token[0]) {
      case '+':
        op = Add;
        break;
      case '-':
        op = Subtract;
        break;
      case '*':
        op = Multiply;
        break;
      case '/':
        op = Divide;
        break;
      default:
        cerr << "Unknown operator!" << endl;
        return -1;
      }
      OP.insert(make_pair(n, op));
    }

    if (!expectNumber) n++;
    expectNumber = !expectNumber;
    nextNonWS = input.find_first_not_of(" ", nextNonWS + token.size());
  }
  
  results = Q(1, V.size(), OP, V);

  cout << "Unique results: ";
  for (set<int>::iterator i = results.begin(); i != results.end(); ++i)
    {
      if (i != results.begin()) {
        cout << ", ";
      }
      cout << *i;
    }

  cout << endl;

  return 0;
}
This is dynamic programming, so the point is that you start by computing small sub-problems, and then save their solutions to later use them to solve large subproblems. The largest subproblem, of course, is the whole problem.

The algorithm, like I posted before, is this:

quote:

Parse the input set so that V(n) is the value of the nth integer, and OP(n) is the operator appearing after the nth integer. Let N be the number of integers in the equation. Now define a function Q such that:

Q(n,n) = V(n)
Q(n,m) = { All unique values you can get by parenthesizing terms n through m }

The missing piece is:

Q(n,n+1) = { V(n) OP(n) V(n+1) }
Q(n,n+k) = { x OP(y) z | n <= y < n+k, x in Q(n,y), z in Q(y+1, n+k) }

Here, our subproblems are continuous runs of adjacent terms. We start with single terms, then we use those answers to build the answers to pairs of terms, then we use those answers to build the answers to four terms in a row, etc, etc.

In english, the above equations for Q mean:

1. The only possible result from parenthesizing a single term is itself, so Q(n,n) = V(n).

2. The only possible result from parenthesizing a pair of terms is the operation between them performed on the two terms, so Q(n,n+1) = { V(n) OP(n) V(n+1) }

3. The possible results for parenthesizing terms n through n + k are computed as follows: For each term y between n and n + k, consider all of the possible results for parenthesizing the terms n to y and y+1 to n+k. For each pair of possible results, one from the left and one from the right, perform OP(y) on these results and add them to the set of possible results for parenthesizing n to n+k, excluding duplicates.

The code above simply implements this definition of the function Q, and caches the results so that no sub-problem is computed more than once (Note that in C++, sets automatically eliminate duplicates). Then, main just parses the input string into terms and operations, and calls Q(1,N) to get the answer set.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

litghost posted:

I am sorry to say this completely wrong. C++ lets you do terrible terrible things sometimes:

code:
	Aclass * bob = (Aclass*)0;
	Aclass & ref_bob = *bob;

Before you even touch ref_bob, you dereference of a null pointer, which is undefined behavior. You can't blame the language for anything that happens after you do something undefined. If your compiler happens to allow the operation to proceed and segfault on a reference later, that's fine, but that doesn't have anything to do with how references are defined to work in C++.


Edit: And as far as the heap corruption trick, references are guaranteed not to be null; they're not guaranteed to always be valid (try returning a reference to something on the stack for a similar effect).

Smackbilly fucked around with this message at 16:33 on Jul 22, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

litghost posted:

But the code compiles and runs (and dies horribly). My point was not that you should be able to do these thing, but that they are possible. You CAN have a reference to a NULL pointer, you can hold a reference to de-allocated memory. In short:

Just because your code compiles and runs doesn't mean the code is valid C++. The C++ standard leaves a number of things "undefined", and if you do any of those things your code is no longer valid C++ and all bets are off. The canonical admonition is "undefined behavior can do anything, including doing nothing, crashing your program, or erasing your hard drive."

If anything your complaint is that your compiler is not smart enough to detect that what you have written is not C++ and yet attempts to compile it as C++ anyway.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Whilst farting I posted:

Thanks for the explanation! I wasn't sure where exactly the pointer would be bound, thank you very much!

I've updated the rest of my code to reflect this, but I'm not sure I'm doing it right. Do you have any good resources you could point me to about multi-dimensional vectors and pointers?

Also, would this be the right way to declare the iterator now?

vector< vector <string>* >::iterator *tableItr;

This is the only way I can get it compile without issue (except for the line with the alphabetical comparison, which I don't know how to fix) and it does not look right at all :psyduck:

code:
for (*tableItr = twoDimStrVec->begin(); !insertComplete && *tableItr != twoDimStrVec->end(); tableItr++)
{
           if (vecRows[0] < &twoDimStrVec[i][0])
           {
                    twoDimStrVec->insert(*tableItr,&vecRows);
                    insertComplete = true;
           } 

           else if (*tableItr == (twoDimStrVec->end() - 1))
           {
                    twoDimStrVec->insert(*tableItr,&vecRows);
                    insertComplete = true;
           }

           else 
                    i++; 
} // end for-loop 

Just making changes until you get the code to compile doesn't really help you much. I can almost guarantee this code will crash immediately. I'll try to go through this error by error:


code:
// Assuming type: vector< vector <string>* >* twoDimStrVec
// Assuming type: vector<string> vecRows

[b]// This declares tableItr as a pointer-to-an-iterator but allocates
// no memory for the actual iterator[/b]
vector< vector <string>* >::iterator *tableItr;

for (*tableItr = twoDimStrVec->begin(); 
!insertComplete && *tableItr != twoDimStrVec->end(); 
tableItr++) [b]// here you are incrementing the POINTER not the iterator it points to[/b]
{
           [b]// twoDimStrVec is type pointer-to-vector<pointer-to-vector<string>>[/b]
           [b]// twoDimStrVec[i] is the ith offset from the pointer above. You're not[/b]
           [b]//   dereferencing the pointer, so it takes the offset of the pointer[/b]
           [b]//   as if it were a pointer to the first element of an array. The result is a[/b]
           [b]//   vector<pointer-to-vector<string > >, as if it were the ith elemenent in[/b]
           [b]//   an array of vectors of pointers to vectors of strings. But twoDimStrVec is[/b]
           [b]//   not an array, so it's dereferencing some random memory location[/b]
           [b]// twoDimStrVec[i][0] is the 0th element of the vector which supposedly exists at[/b]
           [b]//   that random memory location, a pointer to a vector of strings.[/b]
           [b]// &twoDimStrVec[i][0] is the address of that vector that doesn't exist [/b]
           [b]// I'm assuming vecRows is type vector<string> so here you're comparing a string to[/b]
           [b]//   the address of a pointer to a vector of strings[/b]
           if (vecRows[0] < &twoDimStrVec[i][0])
           {
                    [b]// Here you modify the container you are iterating through, which[/b]
                    [b]//   invalidates your iterators. I know you are going to exit the loop[/b]
                    [b]//   right away because you set insertComplete to true, but the increment[/b]
                    [b]//   statement still executes with undefined behavior. Why not use a[/b]
                    [b]//   break statement instead?[/b]
                    twoDimStrVec->insert(*tableItr,&vecRows);
                    insertComplete = true;
           } 

           else if (*tableItr == (twoDimStrVec->end() - 1))
           {
                    [b]// Same as above[/b]
                    twoDimStrVec->insert(*tableItr,&vecRows);
                    insertComplete = true;
           }

           else 
                    i++; [b]// Why are you using i to index things when you have an iterator?[/b]
} // end for-loop 
Try something like this:

code:
// Assuming type: vector< vector <string>* >* twoDimStrVec
// Assuming type: vector<string> vecRows

vector< vector <string>* >::iterator tableItr; // Just an iterator, not a pointer

for (tableItr = twoDimStrVec->begin(); tableItr != twoDimStrVec->end(); tableItr++)
{
           // tableItr is an interator-into-vector<pointer-to-vector<string> >
           // *tableItr is the current pointer-to-vector<string> that you are iterating on
           // **tableItr is the current vector<string> being pointed to
           // (**tableItr)[0] is the first string in that vector
           if (vecRows[0] < (**tableItr)[0]) 
           {
                    twoDimStrVec->insert(tableItr,&vecRows);
                    break;
           } 

           else if (tableItr == (twoDimStrVec->end() - 1))
           {
                    twoDimStrVec->insert(tableItr,&vecRows);
                    break;
           }
} // end for-loop 
Edit: Oh duh, this is much simpler hurrr. Fixed.

Now I don't see why twoDimStrVec has to be a pointer, but if you make it not be a pointer (i.e. just a plain vector<vector<string>*> instead of a vector<vector<string>*>*), all you need to do is change the parts that say "twoDimStrVec->" to say "twoDimStrVec.", and everything else can stay the same, like this:


code:
// Assuming type: vector< vector <string>* > twoDimStrVec
// Assuming type: vector<string> vecRows

vector< vector <string>* >::iterator tableItr; // Just an iterator, not a pointer

for (tableItr = twoDimStrVec.begin(); tableItr != twoDimStrVec.end(); tableItr++)
{
           // tableItr is an interator-into-vector<pointer-to-vector<string> >
           // *tableItr is the current pointer-to-vector<string> that you are iterating on
           // **tableItr is the current vector<string> being pointed to
           // (**tableItr)[0] is the first string in that vector
           if (vecRows[0] < (**tableItr)[0]) 
           {
                    twoDimStrVec.insert(tableItr,&vecRows);
                    break;
           } 

           else if (tableItr == (twoDimStrVec->end() - 1))
           {
                    twoDimStrVec.insert(tableItr,&vecRows);
                    break;
           }
} // end for-loop 

Smackbilly fucked around with this message at 02:38 on Oct 12, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

quote:

While that really compact for-loop looks appealing, it won't run if it's empty - there's nothing in the table yet. The beginning is the end. I added a check to see if it was empty and made it into an if-else statement. But there's a problem. I added a lot of other code just to make sure the problem wasn't the method - it happens no matter which method I use, as long as it involves that specific for-loop and comparison.

<snip>

Here's the problem: you're re-using the same vecRows object over and over again for each row, and you're storing a pointer to it in the table. When you insert a pointer to the vecRows object into the table, you are not copying the contents of the vecRows vector into the table; you are only pointing back to it. So if you insert a pointer to vecRows into table slot 0, that means that when you later retrieve the contents of table slot 0, it just follows the pointer and reads whatever is currently in the vecRows object that you have a pointer to.

So when you insert a pointer to the vecRows object, and then you then overwrite the vecRows object's contents with the next row, dereferencing the pointer in the table will then get you the new data in vecRows (because remember no copy was ever made). In short: because the table contains a pointer to the row vector, changing the contents of the row vector changes the contents of the table.

This problem is really easy to avoid in the following manner: don't use pointers. There is absolutely no reason for you to be using pointers here. When I saw the code snippet you posted earlier, I assumed that there was some good reason why twoDimStrVec was a vector of pointers-to-vectors rather than a vector of vectors. I looked back through the thread to your initial post, and there's no reason at all for that to be the case.

Just do this and all will be well:
code:
[b]// Assuming type: vector<vector<string> > twoDimStrVec[/b]
[b]// Assuming type: vector<string> rowVec[/b]

while (data to be read)
{

vector< vector <string> >::iterator tableItr = twoDimStrVec.begin();

if (twoDimStrVec.empty())
{
    twoDimStrVec.insert(tableItr, vecRows); // Note the lack of &
}

else
{
	for (tableItr = twoDimStrVec.begin(); tableItr != twoDimStrVec.end(); tableItr++)
        {
           // Note *tableItr instead of **tableItr, because there's no pointer
           // involved, just getting the value of the current iterator position
           if (vecRows[0] <= (*tableItr)[0])
           {
                 twoDimStrVec.insert(tableItr,vecRows); // Note no &
                 break;
            }

            else if (tableItr == (twoDimStrVec.end() - 1))
            {
                twoDimStrVec.insert(tableItr, vecRows); // Note no &
                break;
            }
     } // end for-loop
} // end else
}// end while
Even simpler than what you had before, and it has the semantics you want. When you do twoDimStrVec.insert(tableItr, vecRows), it makes a copy of the contents of vecRows and puts that data directly into twoDimStrVec. Since you now have a copy of the contents in the table, rather than a reference back to the row vector, you can now overwrite the contents of the row vector without changing the contents of the table.

Almost all of your confusion here seems to be because you are really eager to use lots of pointers when they are not necessary. No part of this process that we are helping you debug requires the use of any pointers. I suggest that you do some further reading on pointers (even the Wikipedia article or something) to help yourself understand what pointers really do and when/why they are necessary.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Whilst farting I posted:

It needs to be a vector of pointers to vectors of strings, though - this file is massive. This hit the nail on the head as to why I need to do it:

Well, first of all I'm not convinced that using pointers will really make your program go all that much faster. This program is executing a loop of the form 1. read some data from disk, 2. move some stuff around in memory. Disk I/O is exponentially slower than memory access, so 90%+ of your program's time is going to be spent on step (1) rather than step (2) and making step (2) faster won't help all that much.

But if you insist on using pointers, what you have to do is make vecRows into a pointer, and allocate new memory for it each time you start reading a new row. Declare vecRows as type vector<string>*, and replace the vecRows.clear() line with vecRows = new vector<string>().

Then, update the rest of the method so that vecRows[0] becomes (*vecRows)[0] (dereference the pointer to get the vector it points to before using the [] operator) and &vecRows becomes vecRows (no longer need to take the address, since vecRows is now itself a pointer).

Now since you are allocating a new vector every time you read a row, you are no longer overwriting the contents of the vector that you have already stored a pointer to in the table.

The downside of this is that you now have to worry about manually releasing that memory when it is no longer used, or else you will have a memory leak in your program. At minimum, this means defining a destructor for your class and having it iterate through twoDimStrVec, and call "delete *tblItr" (assuming that's what you call the iterator again. If your class provides a method to remove rows from the table, then you will have to call delete on the row pointers when you remove them from the table as well.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Chuu posted:

I've been doing Java mainly for the last couple years, and apparently did well enough on the interview to land a job . . . mostly in C++. I'm really struggling with a few things I seemly completley forgot. Some questions:

Pretend we have a generic object "Object," coded in c++.

1. In java, Object o; would always be a declaration. I understand that in c++ in the source file this is equivalent in java to "Object o = new Object()". In the header file though, is this a full instantiation, or just a declaration?

Instantiation. It is vital however to note however that is not equivalent to "Object o = new Object()" in Java. In C++ "Object o;" instantiates an object on the stack - in Java all objects are on the heap, so this is something that has no Java equivalent. In C++ using the 'new' keyword instantiates on the heap (like Java), so even within C++, "Object o;" and "Object* o = new Object();" are very different.

quote:

2. When declaring constructors, I've been told several times never to initialize variables in the body because the virtual lookup table is not guaranteed to be filled, instead to do things like Object::Object() : m_thing(2){}. What if we need conditional logic to initialize some variables though?

Using initialization lists (as in your little snippet) is usually preferred, but in most cases it is not required. It is strictly required only when you are initializing references (which cannot be reassigned in C++) or const variables. Otherwise, it's mainly just a convention, so members that need more logic in their initialization can be initialized in the constructor body.

But I think you're a bit confused because this really has nothing to do with the object's vtable. The rule having to do with vtables and constructors is not to call virtual methods from constructors (since due to the incomplete vtable the call will not do what you want it to). You can do pretty much anything in a constructor body except call virtual methods.

quote:

3. In c++, how do you declare an object without a name? For example, what's the c++ equivalent of "throw new Exception()" in java.

The equivalent to "throw new Exception()" is "throw new Exception()". That's perfectly valid C++ [edit: assuming you have a class called Exception]. Except you will probably want to "throw Exception()" instead in C++ since because C++ has no garbage collector, instantiating your exception on the heap (with 'new') means that at some point you have to explicitly deallocate it (with 'delete'), which can get real tricky real fast with exceptions.

quote:

4. About using delete. std::string a("hi"); delete(&a);. Why does this blow up?

Because a is on the stack, and delete works on things that are on the heap. Only delete things that were instantiated with 'new'. Stack variables are automatically cleaned up when they go out of scope.

quote:

5. Let's say I have something like the following in Java:

code:
This_object_impelments_FOO foo = new This_object_implements_FOO();
FOO thingie = foo;
In c++ let's say I have a pure virtual class FOO. How do I do something similar to this? Every time I try to assign a concrete subclass of FOO to a pure virtual class I get tons of compiler errors.

Remember that in C++ polymorphism only works with pointers and references. When you call "new This_object_implements_FOO()", you generate a pointer to an object of type This_object_implements_FOO, but then you try to assign that pointer to an object on the stack called 'foo', and that fails. This is what you want:

code:
This_object_impelments_FOO* foo = new This_object_implements_FOO();
FOO* thingie = foo;
And remember that since the object that you created with 'new' is on the heap, you have to manually deallocate the memory when you are done with it by calling 'delete' on a pointer to it.

A side note: references and pointers are a bit tricky when coming from Java to C++ because in Java, every object variable is something that's kind of like a C++ pointer and kind of like a C++ reference. It's like a C++ pointer in that you can reassign what object it points to, but it's like a C++ reference in that you do not have to manually deallocate its storage, and you cannot do memory arithmetic with it. But remember that java has absolutely no equivalent to an object-on-the-stack, which is what you get when you write "FOO thingie" instead of "FOO* thingie".

quote:

7. I hope it's obvious what I'm trying to do here. What's the syntax to do it correctly? a->processMessage(std::string("Message")); (I know this is generally very bad form because you might loose the reference to delete the string.)

There's nothing wrong with that. You are not creating a reference to anything there (that's Java thinking, not C++). std::string("Message") creates a temporary string object on the stack, which will go out of scope and be automatically cleaned up when the function call returns.

Also, why are you writing std::string("Message") and not just "Message". There is automatic conversion between string literals and std::strings, so if a function wants a std::string, you can give it a string literal.

Smackbilly fucked around with this message at 03:29 on Oct 25, 2008

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Janin posted:

The size of a class is not variable, but fixed at compile-time. The issue is one of pointers. If you have an object that contains allocated memory but does not define an assignment operator, and then assign it to another object, the two distinct objects will share a memory buffer. This is almost certainly a bad idea.

This describes one of the two problems with assigning objects that are neither POD nor have a specialized operator=, namely the problem that occurs here:

code:
Object a;
Object b;

b = a;
Where if the class Object has a pointer to a heap buffer that is allocated at contruction-time, a and b are now using the same heap buffer. Oops.

But this problem doesn't occur in this scenario:

code:
Object a;

a = Object();
because the right-hand side is a temporary, so a would have a buffer all to itself.

However, both scenarios have a second problem: the left-hand-side destructor (which presumably deallocates the buffer) is not called when you overwrite it through default assignment, so the pointer to the original buffer allocated to a is lost, and you have a memory leak.

This is the solution to both problems:

quote:

To fix it, define operator=() and have it copy the memory buffer into the new object.

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

Running With Spoons posted:

Here's a hard one for you guys :

code:
unsigned long long int nb;
nb = 223344556677;
printf("%llu",nb);
I'm in C, and nb seems to behave like a long and not like a long long, why is that..?

Your compiler might be interpreting the literal as a long and not a long long. Try changing "223344556677" to "223344556677LLU".

Adbot
ADBOT LOVES YOU

Smackbilly
Jan 3, 2001
What kind of a name is Pizza Organ! anyway?

slovach posted:

Why does something like this go into an infinite loop when you enter something over 50 characters?

code:
#include <iostream>

int main()
{
	char input[50];
	do{
		std::cin.getline(input, sizeof(input));
	}while(strcmp(input, "butts") != 0);
	return 0;
}

Take a look here:

http://www.cplusplus.com/reference/iostream/istream/getline.html

quote:

n
Maximum number of characters to store (including the terminating null character).
This is an integer value of type streamsize.
If the function stops reading because this size is reached, the failbit internal flag is set.

When the fail bit is set, all further input actions fail until you clear it (with std::cin.clear())

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply