constant String Memory Usage
-
OK, so what's with the holy war? I haven't checked all messages in this thread, so forgive me if this has been posted already. Why not use something like:
class CStringBuffer
{
public:
CStringBuffer(CString &str, int len = 0)
: m_str(str) { m_buf = str.GetBuffer(len); }
~CStringBuffer() { m_str.ReleaseBuffer(); }
operator LPTSTR() { return m_buf; }
private:
CString &m_str;
LPTSTR m_buf;
};to handle acquisition/release like
CString str;
CStringBuffer buf(str, 100);
sprintf(buf, "%s %d", "Whatever", 666);I almost always use a wrapper class like that for API pairs like GlobalLock/GlobalUnlock and such. Works great! /sven axelsson, sweden
Actually, this is more dangerous in some ways than just using GetBuffer()/ReleaseBuffer() seperately. Consider this code: CString s; ... CStringBuffer buf(s, 100); sprintf(buf, "%s %d", "Whatever", 666); s = "something else"; sprintf(buf, "%s %d", "Whatever", 666); Now, buf could well be pointing off into a dangling pointer. I like to leave GetBuffer()/ReleaseBuffer() as close as possible together to avoid the possibility of this. Of course, this would be no different than reassigning a char * to new data, but because you hide it in a class, it's not quite so obvious.
-
The GetBuffer() pointer is valid until you call ReleaseBuffer. Period. Same as a new'd data is valid until you call delete (or delete []). It's the same concept. As long as you don't call ReleaseBuffer, the pointer can be stored, copied, read from, written to, all without the buffer becoming invalid. *JUST LIKE A NEW'D POINTER* And just like a new'd pointer, you have to be careful that you or another thread doesn't delete it before you're done with it, so you can't call any functions of the CString object once you've called GetBuffer() until you call ReleaseBuffer(). If you're dumb enough to go calling CString functions when you have a pointer to the data, you're too dumb to be using new, since that requires just as much caution. How exactly should I "have a damn well good idea" of how much data is in a registry key before hand? If my program wrote the data, fine, but if I'm reading reg keys that i didn't write, I have no idea. String objects are fairly efficient due to reference counting. I can copy them by value or reference and they're roughly the same. Only if I need to modify the string does it matter. You seem to have these preconcieved notions that all programs should know ahead of time exactly how much memory they need to allocate. That's simply not true of many kinds of apps, especially those that deal with data that were not written by the app. Your argument about know if it's text data or not is irrelevant, since it's quite simple to test the data in the file. My argument about less code is an argument about readability of the code. The less code you have, the easier it is to read (as long as you're not entering the obfuscated C contest) and understand. It has nothing to do with how much code gets executed. There is no difference between NUL and NULL in C++. Both are 0. In C, NULL is a (void*)0, while NUL is a literal 0. As for remembering whether to put a null at the end of the string, there are many C functions which do not put NUL's at the end. strncpy doesn't for instance, and you have to know this to put the null in yourself, while strcpy does. The point is that you *ALWAYS* had best know whether there is a null there or not, or you are incompetant. Even if you don't know, you don't have to guess. Put a null in anyways, whether you need it or not. The documentation is misleading, in that they are saying you don't *HAVE* to put a null in when specifying a length to ReleaseBuffer, not that you aren't supposed to. And what makes you think that using CString w
> The GetBuffer() pointer is valid until you call ReleaseBuffer. Period. > As long as you don't call ReleaseBuffer, the pointer can be stored, copied, > read from, written to, all without the buffer becoming invalid. > *JUST LIKE A NEW'D POINTER* Uh, no... The pointer becomes invalid under certain situations, like reallocation of the internal buffer (assign a much longer string into it), copying another CString into it, etc. (Friendly Tip: test such things before publishing them.) Period. > If you're dumb enough to go calling CString functions when you have a pointer > to the data, you're too dumb to be using new, since that requires just as > much caution. A pointer allocated via "new" does not become invalid the next time "new" is called. Nor does it become invalid after scope where "new" was called ends (unlike a CString object going out of scope). A pointer obtained via GetBuffer() is a different story, even by your own words. This is exactly the reason why the argument about not using GetBuffer got started. Because the pointer returned by that function requires special handling (of the object). > so you can't call any functions of the CString object once you've called > GetBuffer() until you call ReleaseBuffer(). Make up yer mind. Which is it? "until you call ReleaseBuffer()", or "can't call any functions of the CString object"? Talking about not making sense... >> None of what you claim is any more dangerous than calling >> new and delete to create dynamic memory. You (and I both) just mentioned one reason why using GetBuffer() to obtain a pointer is more dangerous than using "new". > How exactly should I "have a damn well good idea" of how much data is in a > registry key before hand? If my program wrote the data, fine, but if I'm > reading reg keys that i didn't write, I have no idea. If you have no idea, then you also have no idea if the data you are going to be reading is binary or not. Hence, using a CString-based buffer, to hold data that could contain NUL characters is stupid. (And, yes, I do know that you can get both bits of info at runtime.) > You seem to have these preconcieved notions that all programs should know > ahead of time exactly how much memory they need to allocate. No, I have experienced notions that you should know (or at least have an idea about) what you are getting yourself into before starting a project. Serious developers know how important research is (or as our local Guru puts it, "Th
-
> The GetBuffer() pointer is valid until you call ReleaseBuffer. Period. > As long as you don't call ReleaseBuffer, the pointer can be stored, copied, > read from, written to, all without the buffer becoming invalid. > *JUST LIKE A NEW'D POINTER* Uh, no... The pointer becomes invalid under certain situations, like reallocation of the internal buffer (assign a much longer string into it), copying another CString into it, etc. (Friendly Tip: test such things before publishing them.) Period. > If you're dumb enough to go calling CString functions when you have a pointer > to the data, you're too dumb to be using new, since that requires just as > much caution. A pointer allocated via "new" does not become invalid the next time "new" is called. Nor does it become invalid after scope where "new" was called ends (unlike a CString object going out of scope). A pointer obtained via GetBuffer() is a different story, even by your own words. This is exactly the reason why the argument about not using GetBuffer got started. Because the pointer returned by that function requires special handling (of the object). > so you can't call any functions of the CString object once you've called > GetBuffer() until you call ReleaseBuffer(). Make up yer mind. Which is it? "until you call ReleaseBuffer()", or "can't call any functions of the CString object"? Talking about not making sense... >> None of what you claim is any more dangerous than calling >> new and delete to create dynamic memory. You (and I both) just mentioned one reason why using GetBuffer() to obtain a pointer is more dangerous than using "new". > How exactly should I "have a damn well good idea" of how much data is in a > registry key before hand? If my program wrote the data, fine, but if I'm > reading reg keys that i didn't write, I have no idea. If you have no idea, then you also have no idea if the data you are going to be reading is binary or not. Hence, using a CString-based buffer, to hold data that could contain NUL characters is stupid. (And, yes, I do know that you can get both bits of info at runtime.) > You seem to have these preconcieved notions that all programs should know > ahead of time exactly how much memory they need to allocate. No, I have experienced notions that you should know (or at least have an idea about) what you are getting yourself into before starting a project. Serious developers know how important research is (or as our local Guru puts it, "Th
>Uh, no... The pointer becomes invalid under certain situations, >like reallocation of the internal buffer (assign a much longer >string into it), copying another CString into it, etc. >(Friendly Tip: test such things before publishing them.) Period. No, the pointer cannot become invalid if you never use any of the CStrings member functions (including operators) while you have a reference to the internal data. As I've already said, none of those operations are valid after you've called GetBuffer() until you've called ReleaseBuffer(). Yes, you could call them, but you could also go and delete a pointer after you've allocated it and then try to use it. Just because the compiler will allow you to do something, doesn't mean it's a valid operation. My personal policy is, once you call GetBuffer(), forget you have a CString object. Don't use it until you've called ReleaseBuffer(). This policy is no different from a policy of never calling delete on a pointer you have a reference to. Further, my policy is to never hold on to a pointer retrieved from GetBuffer for any longer than needed and to always release it in the same scope. Typically this is about 2-3 lines of code between GetBuffer and ReleaseBuffer. Even further, I only do this with local non-static variables, to prevent any possibility of multithreaded corruption. This policy is no more dangerous than using new/delete, and is in fact less dangerous due to the stringent conditions of it's use. >A pointer allocated via "new" does not become invalid the next >time "new" is called. Nor does it become invalid after scope >where "new" was called ends (unlike a CString object going out >of scope). A pointer obtained via GetBuffer() is a different >story, even by your own words. This is exactly the reason why >the argument about not using GetBuffer got started. Because >the pointer returned by that function requires special >handling (of the object). Consider the following code: { char *p = new char; m_p = p; auto_ptr pptr(p); } dosomethingwith(m_p); In other words, you can do stupid things with new/delete just like you can do stupid things with GetBuffer()/ReleaseBuffer(). Here's a better one: char *p = new char[100]; dosomethingswith(p); dosomethingelsewith(p}; void dosomethingwith(char*p) { delete [] p; } Of course it's contrived, but unless you know exactly what a function is doing with a pointer you pass it, it could very well be deleting your pointer and reallocating it. But you ma
-
>Uh, no... The pointer becomes invalid under certain situations, >like reallocation of the internal buffer (assign a much longer >string into it), copying another CString into it, etc. >(Friendly Tip: test such things before publishing them.) Period. No, the pointer cannot become invalid if you never use any of the CStrings member functions (including operators) while you have a reference to the internal data. As I've already said, none of those operations are valid after you've called GetBuffer() until you've called ReleaseBuffer(). Yes, you could call them, but you could also go and delete a pointer after you've allocated it and then try to use it. Just because the compiler will allow you to do something, doesn't mean it's a valid operation. My personal policy is, once you call GetBuffer(), forget you have a CString object. Don't use it until you've called ReleaseBuffer(). This policy is no different from a policy of never calling delete on a pointer you have a reference to. Further, my policy is to never hold on to a pointer retrieved from GetBuffer for any longer than needed and to always release it in the same scope. Typically this is about 2-3 lines of code between GetBuffer and ReleaseBuffer. Even further, I only do this with local non-static variables, to prevent any possibility of multithreaded corruption. This policy is no more dangerous than using new/delete, and is in fact less dangerous due to the stringent conditions of it's use. >A pointer allocated via "new" does not become invalid the next >time "new" is called. Nor does it become invalid after scope >where "new" was called ends (unlike a CString object going out >of scope). A pointer obtained via GetBuffer() is a different >story, even by your own words. This is exactly the reason why >the argument about not using GetBuffer got started. Because >the pointer returned by that function requires special >handling (of the object). Consider the following code: { char *p = new char; m_p = p; auto_ptr pptr(p); } dosomethingwith(m_p); In other words, you can do stupid things with new/delete just like you can do stupid things with GetBuffer()/ReleaseBuffer(). Here's a better one: char *p = new char[100]; dosomethingswith(p); dosomethingelsewith(p}; void dosomethingwith(char*p) { delete [] p; } Of course it's contrived, but unless you know exactly what a function is doing with a pointer you pass it, it could very well be deleting your pointer and reallocating it. But you ma
> No, the pointer cannot become invalid if you never use any of the CStrings member > functions (including operators) while you have a reference to the internal data. I know that, that is what I said. My first response paragraph countered your statement that the pointer is valid until you call ReleaseBuffer(), which, as you just confimed, was incorrect. > My personal policy is, once you call GetBuffer(), forget you have a CString object. > Don't use it until you've called ReleaseBuffer(). [...Other good tips deleted...] That statement, and the others that we both have made show why the use of CStrings in that manner should be avoided. > Consider the following code: [...Deleted...] A valid point, but I was talking direct use, without using a class to wrap the pointer. Besides, if you need auto_ptr to handle cleanup for you, you have no business messing with pointers in the first place! :) >> Make up yer mind. Which is it? "until you call ReleaseBuffer()", >> or "can't call any functions of the CString object"? > > Huh? This statement makes no sense. My original statement makes perfect sense. That was referring to the original "...until you call ReleaseBuffer()" statement. Read it again if you do not understand. > There is absolutely nothing wrong with using CStrings to store strings with embedded 0's. I never said there was. > This difference is irrelevant in any circumstance except when the size of the > data is important. Which also depends on which platforms, compilers, etc. you are working with. As such, you should never assume that any difference is irrelevant. For example, is the size of an "int" the same as the size of a pointer? >This just makes no sense whatsoever. C++ is not a "newbie" language [...] Right! This entire discussion between us is actually about that fact: the original poster had a simple problem with CString. Problems like this one, IMHO, point to a less experienced developer. Also, given the apparent (mis)use of CString, more experience with it would have been helpful. While it was nice to have someone that I can fence with on the other side of this thread, make sure we do not lose sight of the intent in these messages. CString is a useful component of MFC, but it can be *overused* as well as *misused*. We need to not give less experienced developers so much rope that they hang themselves. First teach 'em about *how* CString works internally, and then tell them about "shortcuts" like GetBuffer().