Why = Returns a Reference, Accessing the Recycle Bin, When to Use STL

Paul DiLascia

Download the code for this article: CQA0104.exe (43KB)
Browse the code for this article at Code Center: RECYCLE

Q The MSDN® documentation explains that the default assignment operator returns a reference:

  TYPE& TYPE::operator=(const TYPE&)
  

Why should operator= return a reference? My understanding is that = is a binary operator. One argument is the reference object that is passed, and the other argument is the instance of the class in which operator= is overloaded. I can implement the assignment operator without returning anything (void), and still complete the assignment. Am I right? So why does the default implementation return a reference?

John Jose
India

A If you think about it long enough, the answer is quite simple. The reason operator= returns a reference is so that you can concatenate multiple assignments in one statement.

  TYPE a,b,c,d;
•••
a = b = c = d;


The compiler interprets the previous lines like so:

  a = (b = (c = d));
  

In compiler-babble, assignment is right-associative. Practically speaking, this means that if you want multiple assignments to work as expected, operator= must return something that can itself be the right-hand side (rhs) of an assignment. What else to return but a reference to the object itself? That's why the last line of operator= always returns a reference to this:

  CMyClass& CMyClass::operator=(const 
CMyClass& rhs) {
••• // do the
// assignment
return *this;
};


      The rhs argument is declared const to allow assigning of const objects, which there is no reason to disallow. And why does operator= return a non-const reference? So you can use an assignment statement wherever a reference to TYPE could be used. For example:

  void MyFunc(TYPE& a);
•••
TYPE a,b;
MyFunc(a=b); // Passes a after assigning.


      Since operator= returns a non-const, you can even use parentheses to override the normal associativity of =.

  TYPE a,b,c;
(a = b) = c;


      Figure 1  shows a dopey little program to demonstrate this. Pop quiz question: what will foo print when you compile and run it?
      If you want to learn more about assignment operators and other such topics, I highly recommend the book Effective C++ by Scott Meyers (Addison Wesley Longman, 1997).

Q I want to delete a file using CFile::Remove (or something else), but I want to send it to the Recycle Bin instead of permanently deleting it so my user can choose to restore it later. Is there any straightforward way to achieve this? If not, I'd like to copy my file to the Recycle Bin instead. So my question is, how do I access the Recycle Bin programmatically?

Amit Kulkarni
India

A The answer to your quandary is a shell API function called SHFileOperation, defined in shellapi.h. To use SHFileOperation, you fill out a special SHFILEOPSTRUCT structure (see Figure 2 ) that tells Windows® what operation to perform, which files to delete, and other important info. SHFileOperation lets you copy, delete, move, or rename one or several files. What's special about SHFileOperation is that it's a high-level shell thing, not a low-level file thing. When you copy files with SHFileOperation, the proper shell copy handlers (if any) are invoked. And, as it says in the documentation: "When used to delete a file, SHFileOperation will attempt to place the deleted file in the Recycle Bin."
      Of course, if you try it at home, it probably won't work the first time. That's because the documentation doesn't tell you that you also have to set the mysterious FOF_ALLOWUNDO flag. It makes perfect sense in retrospect: copying to the Recycle Bin is a way of letting users undo the delete. But when you're figuring things out the first time, the documentation is nothing if not cryptic. You have to read it the way you'd parse an economic pronouncement from Alan Greenspan. Which is to say, very carefully.
      In this case, the relevant passage is a remark about unqualified (relative) file names that says, "If pFrom is set to a filename, deleting the file with FO_DELETE will not move it to the Recycle Bin, even if the FOF_ALLOWUNDO flag is set. You must use a full pathname." From which a meticulously mindful reader may infer: if the FOF_ALLOWUNDO flag is set and the path name is fully qualified, then SHFileOperation will move the file to the Recycle Bin.
      In fact, to recycle a file, you must use a fully qualified path name and FOF_ALLOWUNDO. To show how it all works in real life, I wrote a little class called CRecycleFile that recycles a file (see Figure 3 ), and a console app called RECYCLE that uses it (see Figure 4 ). RECYCLE is way cool. It lets you recycle files from an MS-DOS® command window. Figure 5 shows it in action. If you use a command shell like 4DOS or 4NT, you can even alias "del" to "recycle," thereby turning the MS-DOS del command into a file recycler instead of a deleter. This has saved my butt on many occasions!

Figure 5 RECYCLE in Action
Figure 5 RECYCLE in Action

      I designed CRecycleFile to be easy to use for dumb programmers like me. Here's how:

  LPCTSTR pszPathName = GetFileNameSomehow(); // full path name!
CRecycleFile rf;
rf.Recycle(pszPathName);


What could be easier than that? CRecycleFile is derived from SHFILEOPSTRUCT, so you can tweak its parameters if you like. For normal recycling, there's no need since the constructor initializes SHFILEOPSTRUCT with good defaults. CRecycleFile::Recycle does the deedâ€"that is, sends the file to the Recycle Bin. The hardest part (once you learn the magic FOF_ALLOWUNDO flag) is copying the caller's string into a buffer just to add another zero at the end. Sheesh, what a pain! SHFileOperation lets you delete many files at once; you must concatenate your file names one after another with an empty string (two NULLs) at the end. Fortunately, CRecycleFile performs this dull chore so you don't have to. CRecycleFile makes recycling a piece o' cake!

Q I have read in various places about the Standard Template Library (STL). Should I be using STL or MFC in my app? It will be used for strings, vectors, and things like that. What are the implications of using STL versus MFC?

Maurice Metzgel
Encore Systems

A The answer to all "which should I use?" questions is always the same: it depends on what you want to do, what kind of app you have, and what you know. The last qualification is not to be underestimated. When you need to do something in a jiffy, it's perfectly acceptable to reach for the tool you understand best. If you want to manipulate some text and you know MFC, CString does the trick. If you know STL, use string instead. In the cosmic scale of things, it doesn't much matter which you choose. One string, list, or bag class is pretty much like another. However, there are times when one or another system works better. For example, in my RECYCLE program from the previous question, I used STL. Why?
      When I first wrote RECYCLE, I implemented it as an MFC console appâ€"only because I already had a template console app written. But after reviewing my code (something you should do frequently), I realized that all I used MFC for was CString and CStringList. As it parses its command-line arguments, RECYCLE builds a list of CString names of files to delete. It seemed wasteful to link MFC just for strings and lists. CStringList brings in all of afxcoll.obj, CString pulls in strcore.obj, and AfxWinInit brings whatever initialization modules it requires. You never really know what MFC brings unless you look at the map files. But even without looking, I knew RECYCLE was a situation where STL would work more efficiently.
 To convert to STL and remove all traces of MFC, all I had to do was change a few lines. First, I #included <string> and <list>, then added a typedef for convenience.

  // list of strings
typedef list<string> CStringList;


      Only the name is the same as MFC; the interface is totally different. In particular, STL uses iterators instead of POSITIONs.

  CStringList files; // list o' file names
•••
CStringList::iterator i;
for (i=files.begin(); i!=files.end(); i++) {
•••
}


      Between you and me, I find STL iterators easier to remember than MFC's POSITIONs. For some reason, I can never remember exactly how POSITIONs work and I always find myself consulting the manual, whereas the begin/end and i++ syntax flows easily from my fingertips. On the other hand, I wish STL strings had a conversion function to LPCTSTR, like CString has.

  CString s;        // MFC
LPCTSTR pstr = s; // calls "CString::operator LPCTSTR() const;"


      MFC's conversion function is cool. It lets you pass a CString anywhere you could pass a pointer to a C string. It lets you write

  CString s = "whatever";
MyFunc(s); // MyFunc wants LPCTSTR



whereas with STL, you have to explicitly invoke string::c_str.

  string s;MyFunc(s.c_str());
  

      Perhaps the STL designers figured that conversion functions are obfuscating, which is true but ironic given the overall design of STL. Once again, it hardly matters as far as the code is concerned, it's mainly a matter of typing.
      A more important reason for using STL is, of course, portability. STL is a standard part of C++ the same way printf, tolower, strcpy, and so on are part of C. I've always felt the portability argument is a bit lame since most programs are highly platform-dependent anyway. What other operating systems have SHFileOperation? (Hint: none.) Nevertheless, it's morally satisfying to compartmentalize your platform dependencies to the smallest number of locations, and STL can help you do it. Any compiler that wants to claim ANSI compliance must support STL. Alas, the support isn't always stellar. In particular, Microsoft seems to view STL as a burden to bear rather than a cool technology to embrace. This is evident from the extremely skimpy and often downright incorrect documentation. In some cases, the docs go no further than source code.
      On the other hand, and to be fair, STL really is cryptic, with its partitions and generators and associative containers. And while template code is always startling to behold (just look at ATL!), STL is a serious contender for the all-time unreadability prize. But the Unix guys knew what they were doing, and if you can get past STL's ponderous terminology, unexpected function names, and terse-to-a-T no-white-space code, it all makes beautiful sense. Soon you'll get the hang of it and discover how surprisingly powerful and easy to use STL can be. STL follows the Unix tradition of systems like SED, AWK, and Emacsâ€"hard to learn, but powerful and easy to use. (I should probably confess I'm a die-hard Emacs user.) If you want to be a real geek, you gotta know STL!
      Where can you learn more? There are plenty of places on the Web. Just search for Standard Template Library (duh). One of the most popular sites is https://www.sgi.com/tech/stl/. You'll find intelligible documentation and a FAQ page. Happy programming!
Got a question? Send questions and comments to cppqa@microsoft.com.**

Paul DiLascia is the author of*Windows++: Writing Reusable Windows Code in C++ *(Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or https://www.dilascia.com .

From the April 2001 issue of MSDN Magazine.