|
Hello everyone,
Here is a sample from Dr. Dobb C++. In the analysis, the code is bad below.
But I do not think the code is bad,
1. if bad_alloc is thrown in new int[], we just catch it and write some log;
2. if there are any exception in B's constructor, we will also be in catch block and we could also write some log.
Why it is bad code? Any comments?
(I do not agree that there is resource leak, since if we met with bad_alloc in new int[], there is no memory allocated at all, so no root of memory/resource leak).
http://www.ddj.com/cpp/184401297
C::C(int)
try
: B(new int[n])
{
...
}
catch(Error &e)
{
}
thanks in advance,
George
|
|
|
|
|
IMHO the problem is: if something go wrong inside B constructor the will reach the catch block, where there is no chance to release the acquired resource (i.e. allocated memory).
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Hi CPallini,
I think you mean the memory allocation of new int[] is successful, but in somewhere in constructor, there is an exception, right?
I think in this case, we can use a catch block in B's constructor to invoke delete[] to free memory, and then re-throw the exception to constructor of class C.
Any comments? Please feel free to correct me if I am wrong.
regards,
George
|
|
|
|
|
My doubt is that, if B swallow the exception in its ctor, how C can know that same? B will be partially constructed without knowing that, C completed it's initialization. Those who using the class, C misunderstand that the whole construction was successful and will start using partially constructed objects. I think the error handling can't be done properly if we approach this method. Correct me if I'm wrong.
-Sarath.
"Great hopes make everything great possible" - Benjamin Franklin
|
|
|
|
|
Hi Sarath,
If there are any exception in B's constructor, the exception will be thrown to C's constructor. Why do you say,
Sarath. wrote: B will be partially constructed without knowing that, C completed it's initialization.
and,
Sarath. wrote: misunderstand that the whole construction was successful and will start using partially constructed objects.
regards,
George
|
|
|
|
|
Suppose you cannot change B class, B constructor hasn't a catch block able to face such a situation (after all B class doesn't even know that memory is allocated on the heap), and C class cannot access B internal data (OOP information hiding, he he ).
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Hi CPallini,
I think when there is exception in B's constructor, the exception will also be thrown to C's constructor. So, C will know its sub-components failed to be initialized in C's catch handler block. Do you agree?
(So, C will always be aware of that whether or not B's constructor fails with exceptions)
regards,
George
|
|
|
|
|
George_George wrote: I think when there is exception in B's constructor, the exception will also be thrown to C's constructor.
...
George_George wrote: (So, C will always be aware of that whether or not B's constructor fails with exceptions)
Nope:
If B handles the exception and doesn't throw again it (or a new exception) then C has no chance to handle it.
George_George wrote: So, C will know its sub-components failed to be initialized in C 's catch handler block
Even if C can handle the exception, it cannot delete the array allocated with new if C cannot access the B data members.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Sorry, CPallini.
I may not make myself 100% understood. I mean we catch exception in B's constructor function try block handler, and free the memory in the handler, then re-throw the exception to C. Do you think it is good approach?
regards,
George
|
|
|
|
|
Nope.
B constructor has no knowledge about memory allocation of the passed pointer
(B can't even tell if it was allocated on the stack or on the heap) so it must not delete it. On the other hand, C , even if involved by a B re-thrown exception, cannot delete such memory having no access to B data members.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
Maybe I have not made my ideas clear enough. Here is the pseudo code. Please let me know what is wrong with the code.
class B
{
private:
int* p;
public:
A (int* input) : p(input)
try
{
}
catch (bad_alloc)
{
}
catch (...)
{
if (p) delete[] p;
}
}
regards,
George
|
|
|
|
|
George_George wrote: Maybe I have not made my ideas clear enough.
No, IMHO I've understood your ideas .
The trouble with your code come out whenever you use it the following way:
int foo[100];
B(foo);
If an exception happens inside B constructor, you'll try to delete memory allocated onto the stack!
My point is: your code is specialized to handle that particular weird situation, but it can't cope with other (more simple and reasonable) ones.
i.e. your code as flaw that my above example detects.
BTW:
George_George wrote: A (int* input) : p(input)
The above should be
B (int* input) : p(input)
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
If according to component design, p is always pointed to memory on heap other than stack, do you think my code is good? Any potential issues?
regards,
George
|
|
|
|
|
Well, forcing the client of B to always use the heap is a very bad policy (even supposing that you are the only client of yoursef: you can forget about it , or you may simply be frustrated by, because, sometimes you may prefer to use the stack).
Anyway it is worth nothing that it doesn't help a lot because you're deleting a memory chunk allocated by the caller on unpredictable (by the caller) grounds: you delete only whenever B constructor raises an exception. This is ugly, since the caller usually (i.e. when all goes fine) relies on that chunk of memory:
int *p;
p = new int[25];
assert(p);
B b(p);
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
I assume that you mean if p always pointed to some object on heap, my code is bug free.
BTW: I agree with your analysis that always making assumption on heap is not a good idea, but in this discussion, I extract my issue in a simple way, and we should discuss under in this simple and special condition.
regards,
George
|
|
|
|
|
George_George wrote: I assume that you mean if p always pointed to some object on heap, my code is bug free.
The above assumption is wrong, since, as I shown, a caller may rely on allocated memory.
int * p;
p = new int[10];
assert(p);
...
...
B b(p);
George_George wrote: I extract my issue in a simple way, and we should discuss under in this simple and special condition.
Under restrictive hypothesis (almost) all will work, for instance, provided B contructor never throws, the original article doesn't make sense (that doesn't mean that restrictive hypothesis are bad, they are one of the foundations of design by contract[^]).
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
Thanks CPallini,
I agree that if we do not make safe data backup, there is a bug and the memory pointed by p is deleted.
But from other point, if the data operator (class B) is destroyed, there is no use of the data from some specific perspective.
regards,
George
|
|
|
|
|
Hi,
I am trying to rename a file. The new name of the file is present in another file. The problem is that the renamed file is having junk characters at the beginning. I opened the file in binary mode and found that the chars corresponded to 0xFF and 0xFE. Can anybody help me out?
Thanks!
The code I have is as follows:
{
int nRetCode = 0;
HANDLE fp = CreateFile(_T("KoreanIconNames.txt"), GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(NULL != fp)
{
WCHAR str[BUFFER_SIZE] = {0,};
DWORD dwNumBytesRead = 0;
DWORD dwNumBytesWritten = 0;
size_t ret = 1;
ReadFile(fp, str, sizeof(str), &dwNumBytesRead, NULL);
HANDLE fpNew = CreateFile(_T("KoreanIconNamesNew.txt"), FILE_ALL_ACCESS, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(NULL != fpNew)
{
WriteFile(fpNew, str, sizeof(str), &dwNumBytesWritten, NULL);
CloseHandle(fpNew);
}
// Rename the file
{
CFile fileRename;
WCHAR *pNewName = str;
pNewName = wcstok(str, _T("\n"));
if(NULL != pNewName)
{
CFile::Rename(_T("Citrix.lnk"), pNewName);
}
else
{
AfxMessageBox(_T("Could not tokenize the file name string") );
}
}
CloseHandle(fp);
}
}
AJ
|
|
|
|
|
I can't exactly understand what you are trying to do.
For copying files you can use CopyFile[^] API.
OK I'm not going into the implementation
In my knowledge it's called Byte Order Mark (BOM) which identifies the type of encoding applied to the (text) file. If you are going to rename the file, you dont have to bother about anything Windows Platform will identify it and gives correct name for that.
Please check this article.[^]
just open notepad and save text in different formats. Then open it in any hex-editor(probably visual studio can help you out) and verify each encoding.
<br />
Byte-order mark Description <br />
----------------------------<br />
EF BB BF : UTF-8<br />
FF FE : UTF-16, little endian<br />
FE FF : UTF-16, big endian<br />
FF FE 00 00 : UTF-32, little endian<br />
00 00 FE FF : UTF-32, big-endian
--Update--
Check Byte Order Mark [^] on MSDN
HTH
-Sarath.
"Great hopes make everything great possible" - Benjamin Franklin
modified on Wednesday, December 26, 2007 11:10:08 AM
|
|
|
|
|
Thanks a lot!! You are of great help!
|
|
|
|
|
Two suggestions:
(1) always check how many bytes you effectively read and wrote (and use that info).
(2) use the debugger.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|
|
Ajay L D wrote: WriteFile(fpNew, str, sizeof(str), &dwNumBytesWritten, NULL);
Should be:
WriteFile(fpNew, str, dwNumBytesRead, &dwNumBytesWritten, NULL);
"Normal is getting dressed in clothes that you buy for work and driving through traffic in a car that you are still paying for, in order to get to the job you need to pay for the clothes and the car and the house you leave vacant all day so you can afford to live in it." - Ellen Goodman
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
Hello everyone,
Please help to comment whether my following understanding is correct,
1. whether or not we are using auto_ptr to allocate new object on heap (using new), there may be bad_alloc exceptions;
2. when we met with such exceptions, we catch it (bad_alloc) and try to mininize the operation in catch handler block (since when bad_alloc occurs, it means memory is running out, we can not do anything complex in handler).
Both are correct? Please feel free to correct me if I am wrong.
thanks in advance,
George
|
|
|
|
|
George_George wrote: 1. whether or not we are using auto_ptr to allocate new object on heap (using new), there may be bad_alloc exceptions;
IMHO this is not true, if you use, for instance, malloc (of course) exception is never thrown.
George_George wrote: when we met with such exceptions, we catch it (bad_alloc)
We have the possibility of handling it.
George_George wrote: and try to mininize the operation in catch handler block (since when bad_alloc occurs, it means memory is running out, we can not do anything complex in handler).
That isn't exactly true: the request may fail for a huge chunk request and anyway, complex things don't necessarily need a lot of memory and you can also rely on previous allocated memory to do complex things. However, IMHO, "be minimalist!" on such situation, it is a good guideline.
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
[my articles]
|
|
|
|
|