280 likes | 675 Views
auto for Type Declaration. Auto variables have the type of their initializing expression: auto x1 = 10; //either syntax is auto y1(20); //fine map< int , string> m; auto i1 = m.begin (); Const/volatile and reference/pointer adornments may be added:
E N D
auto for Type Declaration • Auto variables have the type of their initializing expression: auto x1 = 10; //either syntax is auto y1(20); //fine map<int, string> m; auto i1 = m.begin(); • Const/volatile and reference/pointer adornments may be added: const auto *x2 = &x1;const auto& i2 = m; //i2 is map&
auto for Type Declaration • Type deduction for auto is akin to that for template parameters. • Auto can be used to declare multiple variables: void f(string& s){ auto temp = s, *ptr =&s;} • Each initiation must yield the same deduced type (like template deduction): autoi = 5, d = 10.33; //error
nullptr • A new keyword, indicates a null pointer. • Convertible to any pointer type and to bool, but nothing else. • Can’t be used as an integral value • Traditional use of 0 and NULL remain valid. const char * p = nullptr; if (p) int I = nullptr; //error if (nullptr == 0) //error int *p1 = nullptr; int *p2 = 0; int *p3 = NULL; if (p1 == p2 == p3) …
nullptr • Only nullptr is unambiguously a pointer: void f(int *ptr); void f(int val); … f(nullptr); //f(int*) f(0); //f(int) f(NULL); //probably f(int)
How Smart Are your pointers? • C++98 • auto_ptr • TR1 • shared_ptr and weak_ptr • C++0x • Support for allocators and unique_ptr
auto_ptr auto_ptr is a smart pointer template that provides auto destruction of dynamically allocated memory when the pointer goes out of scope. #include <memory> // auto_ptr’s header class MyValue { . . . }; // Same as before void process(int val) { . . . } // Same as before
auto_ptr int main() { std::auto_ptr<MyValue> p(new MyValue(5)); while(--(*p) >= 0) { std::cout << "Enter a value: "; int value; std::cin >> value; process(value); } // An explicit call to delete is no longer necessary . . . } output: CTOR Enter a value: 6 Enter a value: 23 Enter a value: 17 DTOR
auto_ptr • auto_ptr objects contain an ownership indicator. This is used for management of dynamic memory in the event multiple auto_ptr objects refer to the same element_type object. This is different behavior than conventional deep-copy and reference counting schemes for smart pointers.
auto_ptr usage class Foo { public : Foo(int v); ~Foo(); void display(void); private : int m_val; }; Foo::Foo(int v) : m_val(v) { cout << "ctor - " << m_val << endl; } Foo::~Foo() { cout << "dtor - " << m_val << endl; } void Foo::display(void) { cout << ”Value = ” << m_val” << endl;}
auto_ptr usage int main() { std::auto_ptr<Foo> p1(new Foo(1)); std::auto_ptr<Foo> p2(new Foo(2)); p1->display(); . . . } output: ctor - 1 ctor – 2 Value = 1 dtor – 2 dtor - 1
auto_ptr • Relinquishing ownership without releasing memory... int main() { std::auto_ptr<Foo> p1(new Foo(1)); std::auto_ptr<Foo> p2(new Foo(2)); p1.release(); // Memory leak . . . } output: ctor - 1 ctor – 2 dtor - 2
auto_ptr • The right way to use release()... int main() { std::auto_ptr<Foo> p1(new Foo(1)); std::auto_ptr<Foo> p2(new Foo(2)); delete p1.release(); . . . } output: ctor - 1 ctor - 2 dtor - 1 dtor - 2
Ownership • Transfer of ownership… int main() { std::auto_ptr<Foo> p1(new Foo(1)); // p1 owns the object std::auto_ptr<Foo> p2; p2 = p1; // now p2 owns the object std::auto_ptr<Foo> p3(p2); // now p3 owns it . . . } output: ctor - 1 dtor - 1
Ownership void bar(std::auto_ptr<Foo> ptr) { std::cout << "Some processing" << std::endl; } int main() { std::auto_ptr<Foo> p1(new Foo(1)); std::cout << "Before bar()" << std::endl; bar(p1); std::cout << "After bar()" << std::endl; // What does p1 point to here? . . . } output: ctor – 1 Before bar() Some processing dtor – 1 After bar()
Ownership Change bar()’s signature to this… void bar(std::auto_ptr<Foo> &ptr) { . . . } and the problem goes away… output: ctor – 1 Before bar() Some processing After bar() dtor – 1
Smart Pointers • Smart pointers simplify resource management. • Prevention of leaks. • Don’t have to call “delete” • auto_ptr is constraining • Designed for exclusive ownership. • Has strange copy semantics. • Can’t have a container of auto_ptrs. • A standard shared-ownership smart pointers needed: • Should offer “normal” copy semantics. • Hence may be stored in containers. • Many versions have been created/deployed. • Typically based on reference counting.
shared_ptr • Declared in <memory> • A reference-counting smart pointer. • Pointed-to-resources are released when the ref.count (RC)->0. { shared_ptr<Widget> p1(new Widget); shared_ptr<Widget> p2(p1); p1->doThis(); if (p2) p2->doThat(); p2 = nullptr; … //delete is called automatically }
shared_ptr constructors • Default, copy, from raw pointer shared_ptr<Widget> pw1; shared_ptr<Widget> pw2(pw1); shared_ptr<Widget> pw3(new Widget); //typical shared_ptr<Widget> pw4 = new Widget;//error • From compatible unique_ptr, auto_ptr, shared_ptr or weak_ptr. unique_ptr<Widget> makeUP(); auto_ptr<Widget> makeAP(); shared_ptr<Widget> pw5(makeUP()); shared_ptr<Widget> pw6(makeAP()); shared_ptr<const Widget> pw7(pw3);
shared_ptr Constructors • From this: • It’s a raw pointer, but other shared_ptrs might already exist! shared_ptr<ISomething> Widget::getISomething() { return shared_ptr<ISomething>(this);} //dangerous, could create a new ref cnt! shared_ptr<ISomething> Widget::getISomething() { return shared_from_this();} //Okay • Inheritance from enable_shared_from _this is required: class Widget: public ISomething, public std::enable_shared_from_this<Widget> { … };
Some shared_ptr features • Access to underlying new pointer: • Usefule for communicating with legacy APIs. void oldAPI(Widget *pWidget); shared_ptr<Widget> spw(newWidget); oldAPI(spw.get()); • Access to reference count: if (spw.unique()) …. size_t refs = spw.use_count();
shared_ptr and incomplete types • Unlike auto_ptr (but like unique_ptr), shared_ptr supports incomplete types: class Widget; //incomplete type auto_ptr<Widget> ap; //undefined behavior shared_ptr<Widget> sp; //fine unique_ptr<Widget> up; //also fine • shared_ptr thus allows common coupling-reduction strategies. • E.g. pimpl (private implementation)
Pimpl The Pimpl idiom describes a way for making your header files impervious to change. You often hear advices like "Avoid change your public interface!" So you can modify your private interface, but how can you avoid recompilation when your header file defines the private methods. This is what the Pimpl does – Reduce compilation damages when your private interface changes.
Pimpl // In MyClass.h class MyClassImp; // forward declaration of Pimpl class MyClass { public: MyClass (); ~MyClass(); MyClass( const MyClass &rhs ); MyClass& operator=( MyClass ); void Public_Method(); private: MyClassImp *pimpl_; // the Pimpl }; The header defines a forward declaration of the Pimpl and other public interface. It stores a private Pimpl_ member pointer and that is all the client will ever see. There will never be any change unless you change the public interface, which you already know you shouldn't.
shared_ptr and inheritance conversions • auto_ptr fails to support some inheritance-based conversions that shared_ptr offers: class Base {..}; class Derived:public Base {..}; auto_ptr<Derived> create(); void use(auto_ptr<Base>); use(create()); //error shared_ptr<Derived> create(); void use(shared_ptr<Base>); use(create()); //fine
weak_ptr • Weak_ptrs are like raw pointers, but they know when they dangle: • When a resource’s reference count-> 0, its weak_ptrs expire. • Shared_ptr releasing a resouce expires all weak_ptrs shared_ptr<Widget> spw(new Widget); //RC = 1 weak_ptr<Widget> wpw(spw); //RC = 1 if (!wpw.expired()) … //if RC >= 1 • Useful for “observing” data structures managed by others. Safer than raw pointers. Raw pointers might dangle.
weak_ptr • Also to facilitate cyclic structures that would otherwise foil RC: ObjectA Raw pointer shared_ptr ObjectA ObjectB shared_ptr shared_ptr RC friendly, not safe ObjectB RC unfriendly ObjectA weak_ptr shared_ptr ObjectB RC friendly, safer
weak_ptr • Weak_ptrs aren’t really smart pointers. • No deferencing operators (no -> or *) • No implicit nullness test • To use weak_ptr as a pointer, create a shared_ptr from it. weak_ptr<Widget> wpw(spw); wpw->doSomething; //won’t compile //create a shared_ptr; throws if wpw’s expired. shared_ptr<Widget> pw1(wpw); //pw2 is null if wpw’s expired. shared_ptr<Widget> pw2(wpw.lock()); if (pw2) pw2->doSomething();
Cost of shared_ptrs • Sample implementation • 2 words in size (pointer to object, pointer to RC) • Uses dynamically allocated memory for the RC. • Resource release (i.e. deletion) via a virtual function call • Incurs cost for weak_ptr count even if no weak_ptrs are used. T Object Shared_ptr<T> RefCount Weak count Deleter Optional Allocator