Arizona s First University. More ways to show off--controlling your Creation: IP and OO ECE 373
Overview Object Creation Control Distribution Possibilities Impact of design decisions on IP control 2
Good vs. Evil Advantages of Handles Clients have to only re-link to object code (recitative class) Hides all implementation from the end user (good for proprietary code) Clients do not waste time performing recompiles Disadvantages No member functions of the class can be inline All interface classes have this extra impl pointer One extra level of indirection Cost of creating/destroying the handle Evil will always triumph because GOOD is DUMB -- Spaceballs
Release-to-release binary compatibility (RRBC) Using old executable files (dll s and exe s) when portions of the system have changed Objective Adding new member functions or data members should not result in a required recompile You must follow guidelines to do this: Existing class hierarchy cannot change New virtual function declarations must come after the old ones All old virtual functions must remain in the same order (and cannot be deleted) Previously existing public and protected functions may not be deleted The total size of a class instance must remain the same
The Handle See, this is where the handles come in Put all of your changes in with this stuff that people never see Their size will always stay the same Our implementation can change as much as we like We can add member functions to our hearts content.
Object Creation Control My orders are to weed out all non-hackers who do not pack the gear to serve in my beloved Corps. Do you maggots understand that? 6
Object creation control Custom lists may have restrictions Lists of pointers Lists of objects that do not have copy constructors Restriction: DO NOT INSERT OBJECTS CREATED ON THE STACK Why was this? Because the objects get cleaned up when they move out of scope, and you re risking bad memory What did we say? It s hard to enforce that What is the truth? It s pretty easy to enforce that
What did I just do? #ifndef LEC25_DESTRUCTOR_H #define LEC25_DESTRUCTOR_H class PrivatePyle #include "destructor.h" public: #include <cstdio> void publicdelete( ) this->~privatepyle( ); // 3 of the big four are automagic private: ~PrivatePyle( ) ; #endif // LEC25_DESTRUCTOR_H /// shows off using a private destructor int main( void ) PrivatePyle gomer; return 0; Scanning dependencies of target example01 [ 96%] Building CXX object src/lectures/lec25/cmakefiles/example01.dir/example01.cpp.o /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/destructor.h: In function int main() : /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/destructor.h:10: error: PrivatePyle::~PrivatePyle() is private /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/example01.cpp:7: error: within this context make[2]: *** [src/lectures/lec25/cmakefiles/example01.dir/example01.cpp.o] Error 1 make[1]: *** [src/lectures/lec25/cmakefiles/example01.dir/all] Error 2 make: *** [all] Error 2 8
So, again, what did I just do? Make the destructor private This means that when an object goes out of scope, it cannot be deleted Thus, stack created objects will not compile Heap created objects can be compiled, and should be deleted through some special function #include "destructor.h" #include <cstdio> /// shows off using a private destructor int main( void ) PrivatePyle *gomer = new PrivatePyle( ); gomer->publicdelete(); return 0;
Now, the converse How can you restrict usage of the new operator? The same way you get to Congress: Private Practice! #ifndef LEC28_INTHENEW_H #define LEC28_INTHENEW_H // cannot overload the 'new' operator without this header, in linux #include <cstddef> class InTheNew public: InTheNew( ) private: void* operator new( size_t size); void operator delete( void* address ); ; #endif //LEC28_INTHENEW_H
Implementation proof(s) #include "inthenew.h" /// shows off using a private new operator int main( void ) InTheNew *myobject = new InTheNew( ); delete myobject; return 0; Scanning dependencies of target example03 [ 98%] Building CXX object src/lectures/lec25/cmakefiles/example03.dir/example03.cpp.o /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/inthenew.h: In function int main() : /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/inthenew.h:12: error: static void* InTheNew::operator new(size_t) is private /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/example03.cpp:6: error: within this context /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/inthenew.h:13: error: static void InTheNew::operator delete(void*) is private /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/example03.cpp:6: error: within this context /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/inthenew.h:13: error: static void InTheNew::operator delete(void*) is private /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/example03.cpp:6: error: within this context /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/inthenew.h:13: error: static void InTheNew::operator delete(void*) is private /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/example03.cpp:7: error: within this context /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/inthenew.h:13: error: static void InTheNew::operator delete(void*) is private /Users/sprinkle/work/teaching/ece373-2010F/src/lectures/lec25/example03.cpp:7: error: within this context make[2]: *** [src/lectures/lec25/cmakefiles/example03.dir/example03.cpp.o] Error 1 make[2]: Target `src/lectures/lec25/cmakefiles/example03.dir/build' not remade because of errors. make[1]: *** [src/lectures/lec25/cmakefiles/example03.dir/all] Error 2 [100%] Built target example04 make[1]: Target `all' not remade because of errors. make: *** [all] Error 2 make: Target `default_target' not remade because of errors. 11
Implementation proof(s) (example04.cpp) #include "inthenew.h" #include <cstdio> /// shows off using a private new operator int main( void ) InTheNew myobject; return 0; [100%] Building CXX object src/lectures/lec25/cmakefiles/example04.dir/example04.cpp.o Linking CXX executable example04 [100%] Built target example04 But wait...the delete operator is private? How does this work? Of course...this is not the destructor, so the destructor is automatically generated by the compiler. 12
The funny thing... You know what the funny thing is about designing a class hierarchy, replete with class data members, virtual functions, complex attributed return values, and wanting to optimize it given a set of criteria? It s the little differences.
Homework 08 14
Quick and Easy Spec: Give me arbitrarily large sized integer arithmetic (no division) Be fast Be object-oriented Other details Obey all normal conventions for math (op overload) Give me bitwise abilities Dynamically allocate sizes Don t overflow, unless it is for the purpose of 2s complement arithmetic 15
Printing your results So, how would you print an arbitrarily large base 2 representation of an integer? b0001010101010110111110100000101011101010100 0001011111110101011110101011111110000011010110 101010111111010101000011101010101101010101011 10101000001101011010011000111010101011001? Or 3992074717115199917246966599384597530625 249882436953 (in base 10) 16
More on printing in Base 10 In order to print a base 2 number in base 10 form, you need to do the standard 2^0 + 2^1 +... transform, But you also need to be able to represent the base 10 number, which may be arbitrarily sized Rather than represent the base 10 number only as a string, we can do for base 10 what you will do for base 2 This is the hw08 library file, which will be provided online 17
How to use the hw08 library file #include "hpprinter.h"... string str = "b0111111111111111111"; cout << str << " in base 10 is:" << endl; HPPrinter base10( str ); cout << base10 << endl; YOU will need to come up with a way to turn your class into an std::string, and then use the HPPrinter class to turn your string into a base 10 representation. How you do this is UP TO YOU!! How you write your tests is UP TO YOU!! The CMake command to test your file is UP TO YOU!! 18
How hw08 creates Objects So, given a way to control how objects are created, there is a specific need for this assignment: Here is a binary string, give me an HPInt base 10 representation of it Here is a binary number, give me an HPInt base 10 representation of it This can be done with just these two methods... Or, you can have object creation of base10 objects only be possible through these methods. 19
Example: // a fastlist for base_10_lists (i.e., fastlist) // // creates new base_10_list objects, when passed either a string, or an int // If the list already exists (based on the above-defined maps), then // fastlist returns the existing memory structure class fastlist friend class base_10_list; friend class base_10; private: // map of lists created from strings static listmap list; #ifdef FASTLIST_INT // map of lists created from ints static nlistmap nlist; #endif protected: // make these protected so no one can allocate objects // either using the stack, or the new operator fastlist( ); // destructor ~fastlist( ); // give me a list that represents this string static base_10_list* getlist( string str ); #ifdef FASTLIST_INT // give me a list that represents this int static base_10_list* getlist( int n ); #endif // log of the largest value we permit (e.g., the largest power) static float _rooflog; // the default unsigned base prefix static string _unsignedbaseprefix; ; The fastlist class, baby! What does it have that we want? Options (e.g., whether or not to use nfastlist) Speed (statically created, memory management)
Expensive operations Temporary Objects! Owww! You want to avoid these as much as possible Pass by const reference or by const * Do not create any temporary objects that you may need until after you ve done your conditional checks Use the copy constructor whenever possible You can always make this faster using copy-on-write
Examples: // good friend base_10_list operator +( const base_10_list& lhs, const base_10_list &rhs ) base_10_list result = lhs; return result += rhs; // bad friend base_10_list operator +( const base_10_list& lhs, const base_10_list &rhs ) base_10_list result; result = rhs; result += lhs; return result; 22
Proxies Almost, but not quite, exactly like copy-on-write Allows for the passing along of information from some master object that is big and should not be copied lightly, or should be centralized, due to data synchronicity Specifies what should be done to objects when information is written to the base Copy-on-write Specifies that a new object should always be created Pointers Specify that the existing object should be modified??? Specify that under certain conditions the existing object should be modified, but others it should be copied locally
Example: again, with the hw08 class HPInt_10 friend ostream& operator <<( ostream& out, const HPInt_10& me ); public: HPInt_10& operator =( HPInt_10 other ); HPInt_10( const HPInt_10& other ); HPInt_10( int val ); //... HPInt_10& operator-=( const HPInt_10 other ) //... snip *_list -= (*other._list); return *this; friend bool operator==( const HPInt_10& lhs, const HPInt_10& rhs ) //... snip return *lhs._list == (*rhs._list); friend bool operator!=( const HPInt_10& lhs, const HPInt_10& rhs ) //... snip return!(lhs == rhs); friend bool operator>( const HPInt_10& lhs, const HPInt_10& rhs ) //... snip return *lhs._list > (*rhs._list); 24
//... continued from previous page friend bool operator<( const HPInt_10& lhs, const HPInt_10& rhs ) //... snip return *lhs._list < (*rhs._list); friend bool operator>=( const HPInt_10& lhs, const HPInt_10& rhs ) //... snip return *lhs._list >= (*rhs._list); friend bool operator<=( const HPInt_10& lhs, const HPInt_10& rhs ) //... snip return *lhs._list <= (*rhs._list); //... lots more where this came from private: // this list is where all the magic happens base_10_list *_list; ; 25
Making complex from the simple When you are abstracting a system that has easy to understand functionality that builds on itself, take advantage of that Good example: comparative operators // not equal if equal returns false friend bool operator!=( const base_10_list& lhs, const base_10_list& rhs ) return!( lhs == rhs ); // <= if (not >) friend bool operator <=( const base_10_list& lhs, const base_10_list& rhs ) return!( lhs > rhs ); // >= if (not <) friend bool operator >=( const base_10_list& lhs, const base_10_list& rhs ) return!( lhs < rhs );
Distribution Possibilities Optimal delivery methods? 27
Remember the Alamo...uh, compile process 1. Preprocessor 2. Compiler 3. Linker 28
What can you do to protect your internal details? Distribute only compiled and linked code! Works for end users Distribute only compiled code Works for API users You still have to ship header files! How can you avoid putting all your info in header files? Distributed all your headers, and have all functions declared inline Now, you ve lost all your privacy! 29
Example: hiding impl in CPP #ifndef HAPPY_AND_FAST_H #define HAPPY_AND_FAST_H #include <string> class HappyAndFast public: // autogenerate the big four std::string getstring( ); ; #endif #include "happy-and-fast.h" std::string HappyAndFast::getString( ) return "This is my string"; Compile this as a library, and distribute the library files to potential users. Distribute this file, with no inlines 30
Class design impact Using handles/proxies can permit you to hide as much implementation as possible from the user Dynamic library loading (DLL) can allow you to not even have to recompile after libs are updated What are the downsides of distributing libraries? What happens if you want to distribute a templatized class? You re hosed! You can t hide this implementation because template classes cannot be distributed as binary? Why not?? 31