CategoryRefactoring: A special case of ReplaceRecordWithDataClass, for CeePlusPlus. : ''Refactoring a C++ "struct" to be a "class" is trivial: '''Just change your mind.''''' '''Problem:''' : You have a C++ "struct" that you want to start treating more like a class: Typically, you discover methods that would bind tightly with the data (via ExtractMethod, for instance). '''Solution:''' 1. Technically, the only difference between C++ "struct" and "class" is the default member protection: "public:" for struct, "private:" for class. : ''So, there's nothing preventing you from adding methods, or any other "class" feature to a "struct." But it's considered bad form to put code in a "struct," so convert it to a class:'' 2. Painless conversion from "struct" to "class:" (a) change the word "struct" to "class" and (b) add "public:" just inside the '{'. 3. EncapsulateField on each of the member variables. (i.e.: Make them private, and provide accessors.) ---- However, you are changing from a POD type (PlainOldData) to a true class. ''In other languages, yes. But in C++, no. A struct '''is''' a class, and that is the point of this page.'' ''Is it a "good" class that follows best practices? No. So this page is just talking about the initial step of refactoring. Further refactoring should eventually follow...but it needn't be simultaneous with changing your mind that the struct should be a class. That's all.'' EncapsulateField on members will usually require one or several constructors. Therefore: remember to specify proper copying semantics for the class: at least declare a private copy constructor and assignment operator. Consider whether a destructor is desirable. While a virtual destructor belongs to the RuleOfFour(declare either all or none); a value class is seldom a good base class for inheritance. ---- Writing such accessors is lying. It leads you to think the data is really an object, while it is not the case. One should leave them public, until he finds a better abstraction for the data. And if it is data and not an object, I'd leave it a struct. ---- The big change in CeePlusPlus occurs not when you type "class" instead of "struct"; as mentioned above that only changes the default inheritance and access level from public to private. The big change occurs when you do any of the following: * Add a virtual method to the struct/class * Inherit from any struct/class that contains a virtual method * Inherit from any virtual base class. At that point, you essentially change from a PlainOldData struct to a full-fledged object; and thus the following practices are recommended: * Make all functions virtual, unless profiling tells you they shouldn't be; or it is your intent they never be overridden (as C++ lacks "final"; there is no way of enforcing this). * Unless you have '''very''' good reason to keep these around, disable the CopyConstructor and default assignment operator. To do this; just define them to be private, like this: class Foo { private: Foo (const Foo &) {}; Foo &operator = (const Foo &) {}; }; // or in C++11 use class Bar { private: Bar (Bar const &) = delete; Bar (Bar &&) = delete; Bar & operator=(Bar const &) = delete; }; * Make the destructor virtual, even if it does nothing (unless you will be creating/destroying a lot of the object, in which case this might hurt performance. Profiling will tell you that.) ----- As regards disabling the copy constructor and assignment operator, wouldn't it be better to put: class Foo { private: Foo (const Foo &); Foo &operator = (const Foo &); }; i. e., don't provide a blank {} implementation; just declare them. That way, you disable the default versions, and you get a compile error if any code ends up calling them. Whereas if you have a blank implementation, you could end up accidentally calling the copy constructor (more likely than the assignment operator) *from within the class code*, and wonder why you've got a bug (because obviously if it gets called from the class code by accident, then simply making it private won't help you. The only reason I can think of not to do this is if declaring without implementing causes problems on some compilers. ''If one is refactoring a struct in existing code (as is the page premise), one does not need to worry about copy constructors, etc. The operations are already defined and implemented, the existing code is merely being moved into a common class. When refactoring, keep it simple and progress in small steps. Do not throw in code due to text book fears that it might be needed, only add things as they are needed with the appropriate tests to verify operation.'' ---- Compare with UseStructsNotClasses ---- CategoryCpp CategoryRefactoring