80 likes | 94 Views
Advance Tools and Techniques. Overloading new and delete Specialized operators can give more flexibility in how and when memory allocation and de-allocation occur Run-Time Type Identification Can obtain type specific information from an object
E N D
Advance Tools and Techniques • Overloading new and delete • Specialized operators can give more flexibility in how and when memory allocation and de-allocation occur • Run-Time Type Identification • Can obtain type specific information from an object • Can allow access to derived class methods and members through a pointer/reference to a base class • Pointer to Class Member • Can be used to identify a data member or a member function or operator within an object
string *sp = new string ("a value"); string *arr = new string[10]; Three steps occur when new is called A library function named operator new (or operator new[]) is called, which allocates raw, untyped memory from the heap “Placement” version of new is passed pointer to existing memory, and skips this step The appropriate constructor(s) is(are) called to construct the object(s) from the initializers A pointer to the newly allocated and constructed object is returned as the expression’s value delete sp; delete [] arr; Three steps occur when delete is called (inverse of for new) A pointer to the memory to be de-allocated is passed as a parameter A destructor is run at the position (version without []) or destructors are run at all positions (version with []) to the parameter points Memory is returned to the heap by calling library function operator delete or operator delete[] Operators new and delete are Inverses
// might throw an exception void *operator new(size_t); void *operator new[](size_t); void *operator delete(void*) noexcept; void *operator delete[](void*) noexcept; // promise not to throw void *operator new(size_t, nothrow_t&) noexcept; void *operator new[](size_t, nothrow_t&) noexcept; void *operator delete(void*, nothrow_t&) noexcept; void *operator delete[](void*, nothrow_t&) noexcept; // placement new operators don’t allocate: just call // constructor(s) starting at passed address, return it void *operator new(size_t, void*); // can’t redefine void *operator new[](size_t, void*); Interfaces for Operators new and delete
// From LLM pp. 823 void *operator new(size_t size) { if (void *meme = malloc(size)) return meme; else throw bad_alloc(); } void operator delete (void *meme) noexcept { free (meme); } Implementations of Operators new and delete
The dynamic_cast operator returns a pointer or reference to a more specific (derived) type if it can Only can be used with pointers to structs or classes with virtual methods The dynamic_cast operator returns nullptr (or throws an exception) if it cannot downcast to the specified type dynamic_cast<type*> (base) // returns a pointer dynamic_cast<type&> (base) // returns an l-value reference dynamic_cast<type&&> (base) // returns an r-value reference The typeid operator returns a type_info for a given expression Works with built-in types as well as user-declared types Cannot declare or construct type_info objects explicitly Can only use implicitly via expressions involving the typeid operator and operators and methods available for type_info objects, e.g., cout << "e is of type " << typeid(e).name() << endl; cout << " f is of type " << typeid(f).name() << endl; cout << " the types are " << (typeid(e) == typeid(f) ? " " : " not ") << "the same" << endl; Run-Time Type Identification
bool same_cage (Animal * a, Animal * b) { // only ok if both are Pandas return dynamic_cast<Panda *>(a) && dynamic_cast<Panda *>(b); } int main (int, char * []) { Animal *a = new Grizzly (); Animal *b = new Panda (); cout << "we have a " << typeid(*a).name() << endl << "and a " << typeid(*b).name() << endl << endl; if (!same_cage(a, b)) { cout << "Don’t put in same cage!" << endl; } delete a; delete b; return 0; } Run-Time Type Identification Example Output: We have a struct Grizzly and a struct Panda Don’t put in same cage!
struct Truck { Truck (unsigned int w): weight_(w) { cout << "weight: " << weight_ << endl;} unsigned int weight_; }; int main (int, char * []) { Truck trucks [] = {902, 900}; auto weight_ptr = &Truck::weight_; // data cout << "Truck weights are: " << endl; for (Truck* truck_ptr = trucks; truck_ptr - trucks < sizeof(trucks)/sizeof(Truck); ++truck_ptr) { cout << "Truck " << truck_ptr - trucks << " weight is " << truck_ptr->*weight_ptr << endl; } return 0; } Output: weight: 902 weight: 900 Truck weights are: Truck 0 weight is 902 Truck 1 weight is 900 Pointer to Data Member
class Truck { public: Truck (unsigned int w): weight_(w) { cout << "weight: " << weight_ << endl;} unsigned int weight() {return weight_;} private: unsigned int weight_; }; int main (int, char * []) { Truck trucks [] = {902, 900}; // declaration almost identical to previous auto weight_ptr = &Truck::weight; // fxn cout << "Truck weights are: " << endl; for (Truck* truck_ptr = trucks; truck_ptr - trucks < sizeof(trucks)/sizeof(Truck); ++truck_ptr) { cout << "Truck " << truck_ptr - trucks << " weight is " << (truck_ptr->*weight_ptr)() << endl; // note how parens are used } return 0; } Output: weight: 902 weight: 900 Truck weights are: Truck 0 weight is 902 Truck 1 weight is 900 Pointer to Member Function