开发者

The ** idiom in C++ for object construction

开发者 https://www.devze.com 2023-01-02 23:41 出处:网络
In a lot of C++ API\'S (COM-based ones spring to mind) that make something for you, the pointer to the object that is constructed is usually required as a ** pointer (and the function will construct a

In a lot of C++ API'S (COM-based ones spring to mind) that make something for you, the pointer to the object that is constructed is usually required as a ** pointer (and the function will construct and init it for you)

You usually see signatures like:

HRESULT createAnObject( int howbig, Object **objectYouWantMeToInitialize ) ;

-- but you seldom see the new object being passed as a return value.

Besides people wanting to see error codes, what is the reason for this? Is it better to use the ** pattern rather than a returned pointer for simpler operations such as:

wchar_t* getUnicode( const char* src ) ;

Or would this better be written as:

void getUnicode( const char* src, wchar_t** dst ) ;

The most importa开发者_如何学Cnt thing I can think of is to remember to free it, and the ** way, for some reason, tends to remind me that I have to deallocate it as well.


"Besides wanting error codes"?

What makes you think there is a besides. Error codes are pretty much the one and only reason. The function needs some way to indicate failure. C doesn't have exceptions, so it has to do that through either a pointer parameter, or the return value, and the return value is idiomatic, and easier to check when calling the function.

(By the way, there's no universal rule that ** means you have to free the object. That's not always the case, and it's probably a bad idea to use something that arbitrary to remind you of which objects to clean up.)


Two reasons come to my mind.

First are error codes actually. Other than C++, C doesn't have exceptions, and COM is a C-API. Also many C++ based projects prefer not to use exceptions for various reasons. There may be cases, where a return value can't signal errors, E.g. if your function returns an integer, there may be no integer value, that can represent an error code. While signalling errors with pointers is easy (NULL == Error), some API designers prefer to signal errors in a consistent way over all functions.

Second, functions can have only one return value, but calling them may create multiple objects. Some Win32 API functions take multiple pointers to pointers that can be filled optionally, if you call these functions with non-NULL pointers. You cannot return two pointers, or rather this would be awkward to use, if the return value is some struct by value containing more than one pointer. Here too a consistent API is a sensible goal to achieve.


New objects in function arguments passed by ** is better. This take me a comfort to future use of change void to bool for example to return success of a function or other information providing function works.

Answer in one line: This is much better for resulting error codes.


Besides people wanting to see error codes, what is the reason for this?

There are some reasons for this. One of them is writing an interface that is usable in C (you see this in the WinAPI and Windows COM).

Backwards compatibility is another reason (i.e. the interface was written like that and breaking it now would break existing code).

I'd go with C compatibility for a design principle when using code like this. If you were to write in C++ you'd write

retval Myfunction(Result *& output);

instead of

retval Myfunction(Result ** output);

or (even better):

Result *Myfunction();

and have the function throw an exception on error.


I'm not sure I agree that's the best way to do it... this might be better:

Object * createAnObject(int howbig, HRESULT * optPlaceResultCodeHereIfNotNull = NULL);

That way there is no messing about with double-indirection (which can be a little bit tricky for people who aren't used to it), and the people who don't care about result codes don't have to worry about the second argument at all... they can just check to see if the return value is NULL or not.

Actually, since it's C++, you could make things easier still, using function overloading:

Object * createAnObject(int howbig);
Object * createAnObject(int howbig, HRESULT & returnResultCode);


Any method call in a COM call has to be HRESULT. The return codes get leveraged all over the framework and passing a double pointer is a well-known way to get the created object.


Not answering your question but a comment as your question brought out some thoughts I have about COM/DCOM programming using C++.

All these "pointer" and "pointer to pointer", memory management and reference counting are the reasons why I shy away from doing COM programming with C++. Even with ATL in place, I dislike it for the simple reason that it does not look natural enough. Having said that, I did do a few projects using ATL.

Back then the alternative is use VB. VB code looks more natural for COM or DCOM programming.

Today, I would use C#.

0

精彩评论

暂无评论...
验证码 换一张
取 消