From 948744964683dec959bb3f2a9a18903bf20b8cac Mon Sep 17 00:00:00 2001 From: konrad Date: Wed, 29 Dec 2010 11:18:03 +0000 Subject: [PATCH] add more docu; add README for tests; add test for clone of shared dptr; make class include macro more resilient git-svn-id: https://silmor.de/svn/softmagic/chester/trunk@695 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- Doxyfile | 2 +- dptr.dox | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++ dptr.h | 118 +---------------- dptr_base.h | 12 +- dptr_noncopy.h | 4 +- dptr_shared.h | 4 +- tests/README | 9 ++ tests/dptrpriv.cpp | 6 + tests/dptrtest.cpp | 6 + tests/dptrtest.h | 1 + 10 files changed, 417 insertions(+), 125 deletions(-) create mode 100644 dptr.dox create mode 100644 tests/README diff --git a/Doxyfile b/Doxyfile index db527c0..1674663 100644 --- a/Doxyfile +++ b/Doxyfile @@ -598,7 +598,7 @@ INPUT_ENCODING = UTF-8 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 -FILE_PATTERNS = *.h +FILE_PATTERNS = *.h *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. diff --git a/dptr.dox b/dptr.dox new file mode 100644 index 0000000..9a2f16d --- /dev/null +++ b/dptr.dox @@ -0,0 +1,380 @@ +/** \mainpage Chester - d-pointers + +Chester is an implementation of the Cheshire Cat idiom - also known as +d-pointer, PImpl (Pointer IMPLementation), or opaqua data pointer. +It hides the data of a class behind a "d" pointer. + +This implementation takes away much of the bookkeeping work, like allocating +and de-allocating the pointer. + +All the files of Chester are copyrighted under a permissive license: + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. This file is offered as-is, + without any warranty. + +So, you are free to use this implementation in any project under any kind of +license as long as you do not remove my copyright notice. + +Although I do not require it, I would appreciate feedback on problems, bugs, +and the occasional code improvement - if you feel like it... + +Different versions of Chester can be mixed for different classes, but the same version must be used both in the header file and in the implementation of the same class. + +\section kinds Kinds of D-Pointers + +Cheshire cats or d-pointers allow you to hide the data members (and if you want some of the implementation) of a class and also allow (within some constraints) to maintain ABI compatibility (ABI=Application Binary Interface) while adding or removing hidden data members. + +Chester defines three different kinds of d-pointers: non-copy, non-shared, and shared. Those three define different access patterns to the d-pointer. + +Non-copy d-pointers cannot be copied directly, if you want to make your main class copyable you have to explicitly create a copy constructor and/or an assignment operator. Those should be used with main classes and data objects that cannot be copied (like Qt's QObject and its subclasses). + +Non-shared d-pointers can be copied - if you copy an instance of the main class, the content of the d-pointer gets copied automatically (at least if you use the automatic copy constructor or call the copy constructor of the d-pointer). Each copy acts independently. This version produces the same behavior as if the data was defined directly in the main class instead of inside the d-pointer. + +Shared d-pointers can also be copied, but copying shares the data between instances of the main class. This can be used to create interface classes in which all copied instances share their data. + +All three kinds share the usage pattern. The header file and class interface declaration is declared like this (this example uses non-shared d-pointers): +\code +//file: myclass.h + +#include + +//declare main class +class MyClass +{ + //declare d-pointer + DECLARE_DPTR(d) + //declare the remainder of the class minus data + public: + MyClass(); + void setInt(int); + int getInt()const; + int getIntPlusOne()const{return getInt()+1;} +}; +\endcode + +As shown above, methods cannot be defined as inlines if the data is hidden behind a d-pointer, since this would require the compiler to know the structure of the d-pointer. However, it is possible to inline any method that does not access the d-pointer directly. + +The actual implementation file then declares and defines the d-pointer class and defines all methods of the main class that need access to the d-pointer: +\code +//file: myclass.cpp + +#include "myclass.h" +#include + +//declare internal d-pointer class +class DPTR_CLASS_NAME(MyClass):public DPtr +{ + public: + int myint; +}; +//define glue code +DEFINE_DPTR(MyClass) + +//constructor +MyClass:MyClass() +{ + d->myint = 0; +} + +//a setter function (non-const) +void MyClass::setInt(int i) +{ + d->myint = i; +} + +//a getter function (const) +int MyClass::getInt()const +{ + return d->myint; + //this code would not compile: + //d->myint = 7; +} + +//example usage: +main() +{ + MyClass myinst; + myinst.setInt(1); +} +\endcode + +Which actual kind of d-pointer behavior is declared is decided by using different macros and base classes for the d-pointer class. The argument to the DECLARE_* macro defines the name of the d-pointer variable, usually this will be "d", but you can chose any other name (except "Private" and "DPrivate", which are used as wrapper class names). + +The DPTR_CLASS_NAME macro returns the class name of the d-pointer class relative to the main class, which is given as argument. The DEFINE_* macro defines the logic that automatically instantiates, deletes and copies the d-pointer when the main class is instantiated, deleted, and copied. The d-pointer class must be derived from one of the DPtr classes, since these implement some functionality complementing the DEFINE_* macro. + +The header file should only include \ - this file contains the DECLARE_* macros, which is the only thing needed for the header. If you include any other file, this makes mixing versions of Chester impossible in some situations. The implementation file must include one of the d-pointer implementation variants. + +\section table Comparison Table + +The macros, classes, \#include statements above need to be replaced with different ones if you want one of the other kinds of d-pointers: + + + + + + + + + + +
TypePointer Declaration (1)Declaration Include (1) + Base Class(2, 3)Definition (2)Implementation Include (2)
Non-Shared, CopyableDECLARE_DPTR(Class)\#include \<\link dptr_base.h DPtrBase \endlink > + DPtrDEFINE_DPTR(Class) + \#include \<\link dptr.h DPtr \endlink > +
SharedDECLARE_SHARED_DPTR(Class)\#include \<\link dptr_base.h DPtrBase \endlink > + SharedDPtrDEFINE_SHARED_DPTR(Class) + \#include \<\link dptr_shared.h SharedDPtr \endlink > +
Non-CopyableDECLARE_NONCOPY_DPTR(Class)\#include \<\link dptr_base.h DPtrBase \endlink > + NonCopyDPtrDEFINE_NONCOPY_DPTR(Class) + \#include \<\link dptr_noncopy.h NonCopyDPtr \endlink > +
+ +(1) The DECLARE_* macro and declartion include directive are supposed to be used in the header file of the main class. + +(2) The implementation include directive should only be used in the main class'es implementation file - use the DEFINE_* macro to create the glue code that connects the d-pointer class with the main class. + +(3) The base class must be used to derive the d-pointer class in the implementation file. It contains some functionality that the DEFINE_* macro relies on. + +There are three more macros that return default names for d-pointer properties: +- DPTR_CLASS_NAME(Class) returns the name of the d-pointer class for a given main class (which may be namespace qualified) +- \link ::DPTR_NAME DPTR_NAME \endlink returns the local name of the d-pointer class, and its constructor +- \link ::DPTR_WRAPPER_NAME DPTR_WRAPPER_NAME \endlink returns the local name of the wrapper class that contains the glue code to implement the expected d-pointer behavior + +\section nonshare Using Non-Shared D-Pointers + +Using the code above you will get a non-shared d-pointer for your main class. The behavior of your main class will be very similar to the behavior you would get with defining the data inside the main class itself. So this is the normal choice when you transform a normal class into a d-pointer based class. The automatic copy constructor and the automatic assignment operator work exactly as would be expected from a normal class: + +\code +MyClass my1; +my1.setInt(1); +printf("my1 is now %i\n",my1.getInt()); // my1 is now 1 +//create a copy +MyClass my2(my1); +printf("my2 is now %i\n",my2.getInt()); // my2 is now 1 +my1.setInt(2); +my2.setInt(3); +printf("my1 is now %i\n",my1.getInt()); // my1 is now 2 +printf("my2 is now %i\n",my2.getInt()); // my2 is now 3 +\endcode + +If you want to override the copy constructor you have several choices: you can do all the copying yourself in the main class or the d-pointer class, you can use the d-pointers copy constructor or you can use the d-pointers assignment operator: +\code +//version 1: using the copy constructor +MyClass::MyClass(const MyClass©) + :d(copy.d) +{ +} + +//version 2: using the assignment operator +MyClass::MyClass(const MyClass©) +{ + d = copy.d; +} + +//version 3: doing the copying itself in the main class +MyClass::MyClass(const MyClass©) +{ + d->myint = copy.d->myint; +} + +//version 4: d-pointer class with copy constructor: +class DPTR_CLASS_NAME(MyClass):public DPtr +{ + public: + DPTR_NAME(const DPTR_NAME©) + { + myint = copy.myint; + } +} +\endcode + +It is recommended to use the copy constructor, since this most closely models the behavior of the automatic copy constructor and may save some unnecessary initialization of members. The manual copying of members is quite error prone, since it may break if you add members to the d-pointer, but forget to add them in the copy constructor of the main class or the d-pointer class. + +The version of a d-pointer class above uses the DPTR_NAME macro to resolve the local name of its constructor and class name. It is also almost identical to the version that the compiler would have created automatically, but as with the custom versions in the main class above you can change some behavior here. However, since the danger exists to forget some members it is not recommended to do this. Instead it is usually much safer to use version 1 above and add custom code in the body of the copy constructor of the main class. + +For the assignment operator you also have the choice of implementing it in the main class or the d-pointer class, but it is recommended to do it in the main class: +\code +MyClass& MyClass::operator=(const MyClass©) +{ + //use the automatic operator from d-pointer + d = copy.d; + //add custom code here... +} +\endcode + +This leverages the automatic assignment operator of the d-pointer class, eliminating the risk of forgetting members and still allows some customization. + +\section share Using Shared D-Pointers + +When defining shared d-pointers you have to use different macros and a different base class, but otherwise things stay identical: +\code +// file: myfile.h +#include +class MyClass +{ + DECLARE_SHARED_DPTR(d) + public: + ... +}; + +// file: myfile.cpp +#include "myfile.h" +#include + +//define d-pointer class +class DPTR_CLASS_NAME(MyClass):public SharedDPtr +{ + public: + int myint; +}; +//define glue code +DEFINE_SHARED_DPTR(MyClass) +\endcode + +Instances of shared d-pointers access the same members if they have been assigned from each other: +\code +MyClass my1; +my1.setInt(1); +printf("my1 is now %i\n",my1.getInt()); // my1 is now 1 +//create a copy +MyClass my2(my1); +printf("my2 is now %i\n",my2.getInt()); // my2 is now 1 +my1.setInt(2); +my2.setInt(3); +printf("my1 is now %i\n",my1.getInt()); // my1 is now 3 +printf("my2 is now %i\n",my2.getInt()); // my2 is now 3 +MyClass my3; +my3.setInt(99); +printf("my1 is now %i\n",my1.getInt()); // my1 is now 3 +printf("my3 is now %i\n",my3.getInt()); // my3 is now 99 +my3 = my1; +printf("my3 is now %i\n",my3.getInt()); // my3 is now 3 +\endcode + +If instances are never copied or assigned they still are independent, they get linked as soon as assignment happens. Chester keeps a reference counter on the d-pointer automatically allocating and de-allocating it if necessary. + +Even in the case of shared d-pointers it is sometimes desireable to create a "deep copy" - a copy of an instance that is independent from the original. This can be done by implementing a simple clone method: +\code +MyClass MyClass::deepCopy()const +{ + MyClass ret; + ret.d=d.clone(); + return ret; +} + +... +my3 = my1.deepCopy(); +my3.setInt(100); +printf("my1 is now %i\n",my1.getInt()); // my3 is now 3 +printf("my3 is now %i\n",my3.getInt()); // my3 is now 100 +\endcode + +Please note that the clone() method of the d-pointer is called with a dot (.), not an arrow (->) - we are using a method of the pointer-wrapper itself here. Please see below for details. + +Under normal circumstances the copy constructor of the d-pointer class is never used in shared d-pointers, the assignment operator is only used when creating deep copies. The rest of the time the pointer is just passed around. + +\section noncopy Non-Copyable D-Pointers + +When defining non-copyable d-pointers you have to use different macros and a different base class, but otherwise things stay identical: +\code +// file: myfile.h +#include +class MyClass +{ + DECLARE_NONCOPY_DPTR(d) + public: + ... +}; + +// file: myfile.cpp +#include "myfile.h" +#include + +//define d-pointer class +class DPTR_CLASS_NAME(MyClass):public NonCopyDPtr +{ + public: + int myint; +}; +//define glue code +DEFINE_NONCOPY_DPTR(MyClass) +\endcode + +You can use the non-copyable version of Chesters d-pointers if you want to prevent the user from copying instances of your main class or if you want to explicitly implement the copy constructor and assignment operator yourself. If you do implement them you will have to do this in the main class, since the d-pointer-wrapper itself has a built-in barrier to copying - only its default constructor is usable. + +This version is meant for d-pointers serving classes that are inherently un-copyable, like Qt's widgets. + +\section magic Deep Magic: the Inner Workings of Chester + +The DECLARE_* macro of Chester actually defines two internal classes for its purposes: MyClass::Private and MyClass::DPrivate. The MyClass::Private class is the one you define to contain the data and the one that you access through "d->". The MyClass::DPrivate class contains the wrapper code that automatically instantiates and deletes the instance of MyClass::Private. The member variable "d" is actually an instance of DPrivate - however the "->" operator of that class returns a pointer of Private, so that it appears to be a normal pointer to Private. But since no dereference operator (*) is defined you cannot directly overwrite or delete the instance of Private - this serves as a barrier against accidental deletion or non-deletion of the d-pointer (of course you can still get at the pointer with some fancy casting). + +A simplified version of the expanded DECLARE_* macro would look like this: +\code +class MyClass +{ + //DECLARE_*DPTR(d): + class Private; //forward declaration of Private + class DPrivate + { + public: + DPrivate(); //constructor, see below + ~DPrivate(); //destructor, see below + const Private* operator->()const; //access to the pointer, const + Private* operator->(); //access to the pointer, non-const + private: + Private *inner; //actual pointer + }; + DPrivate d; //instance of the d-pointer-wrapper + //end of macro + public: + MyClass(); //... +}; +\endcode + +The DEFINE_* macro then implements the methods declared above: +\code +//DEFINE_*DPTR(MyClass): +MyClass::DPrivate::DPrivate() +{ + inner = new MyClass::Private; +} + +MyClass::DPrivate::~DPrivate() +{ + delete inner; +} + +MyClass::Private* MyClass::DPrivate::operator->() +{ + return inner; +} + +const MyClass::Private* MyClass::DPrivate::operator->()const +{ + return inner; +} +\endcode + +So when accessing "d" directly we access the wrapper class (which does not offer much of an interface), and when accessing it through "d->" we access members of the inner member of the wrapper instead of the wrapper itself. + +There are two versions of the "->" operator. The first one without const is used when the d-pointer is written to, the second version with const is used for pure read operations and inside const methods of the main class. Having those two versions ensures that the members of the d-pointer adhere to the const declarations of the main class, otherwise it would be possible to write to members even if it happens inside a const method. + +The differences between the various versions of the macros are in details not shown above - copy constructors, and assignment operators. + +\section abi A Word about ABI + +If you want to use d-pointers to maintain ABI (Application Binary Interface) compatibility over several versions, you also have to adhere to a few more rules. While adding and removing data members is the most obvious cause of ABI incompatibility, some other changes also influence it. For example: +- changing inheritance +- changing a parent class in an ABI incompatible way +- changing of inline status of a method +- removing methods +- changing a method's signature (changing/adding/removing arguments, access-levels, add/remove const) +- changing return types +- adding/removing virtual methods +- changing the order of virtual methods + +For a more complete article on the topic see for example: +http://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++ + +*/ diff --git a/dptr.h b/dptr.h index 7812a0b..c74ad22 100644 --- a/dptr.h +++ b/dptr.h @@ -9,122 +9,8 @@ #include "dptr_base.h" -#ifndef DPTR_CLASS_H -#define DPTR_CLASS_H - -/** \mainpage Chester - d-pointers - -Chester is an implementation of the Cheshire Cat idiom - also known as -d-pointer, PImpl (Pointer IMPLementation), or opaqua data pointer. -It hides the data of a class behind a "d" pointer. - -This implementation takes away much of the bookkeeping work, like allocating -and de-allocating the pointer. - -All the files of Chester are copyrighted under a permissive license: - Copying and distribution of this file, with or without modification, - are permitted in any medium without royalty provided the copyright - notice and this notice are preserved. This file is offered as-is, - without any warranty. - -So, you are free to use this implementation in any project under any kind of -license as long as you do not remove my copyright notice. - -Although I do not require it, I would appreciate feedback on problems, bugs, -and the occasional code improvement - if you feel like it... - -Different versions of Chester can be mixed for different classes, but the same version must be used both in the header file and in the implementation of the same class. - -\section kinds Kinds of D-Pointers - -Cheshire cats or d-pointers allow you to hide the data members (and if you want some of the implementation) of a class and also allow (within some constraints) to maintain ABI compatibility (ABI=Application Binary Interface) while adding or removing hidden data members. - -Chester defines three different kinds of d-pointers: non-copy, non-shared, and shared. Those three define different access patterns to the d-pointer. - -Non-copy d-pointers cannot be copied directly, if you want to make your main class copyable you have to explicitly create a copy constructor and/or an assignment operator. Those should be used with main classes and data objects that cannot be copied (like Qt's QObject and its subclasses). - -Non-shared d-pointers can be copied - if you copy an instance of the main class, the content of the d-pointer gets copied automatically (at least if you use the automatic copy constructor or call the copy constructor of the d-pointer). Each copy acts independently. This version produces the same behavior as if the data was defined directly in the main class instead of inside the d-pointer. - -Shared d-pointers can also be copied, but copying shares the data between instances of the main class. This can be used to create interface classes in which all copied instances share their data. - -All three kinds share the usage pattern. The header file and class interface declaration is declared like this (this example uses non-shared d-pointers): -\code -//file: myclass.h - -#include - -class MyClass -{ - DECLARE_DPTR(d) - public: - void myFunction(); -}; -\endcode - -The actual implementation file then declares and defines the d-pointer class: -\code -//file: myclass.cpp - -#include "myclass.h" -#include - -class DPTR_CLASS_NAME(MyClass):public DPtr -{ - public: - int myint; -}; -DEFINE_DPTR(MyClass) - -void MyClass::myFunction() -{ - d->myint = 99; -} - -main() -{ - MyClass myinst; - myinst.myFunction(); - MyClass otherinst(myinst); -} -\endcode - -Which actual kind of d-pointer behavior is declared is decided by using different macros and base classes for the d-pointer class. The argument to the DECLARE_* macro defines the name of the d-pointer variable, usually this will be "d", but you can chose any other name (except "Private" and "DPrivate", which are used as wrapper class names). - -The DPTR_CLASS_NAME macro returns the class name of the d-pointer class relative to the main class, which is given as argument. The DEFINE_* macro defines the logic that automatically instantiates, deletes and copies the d-pointer when the main class is instantiated, deleted, and copied. The d-pointer class must be derived from one of the DPtr classes, since these implement some functionality complementing the DEFINE_* macro. - -The header file should only include \ - this file contains the DECLARE_* macros, which is the only thing needed for the header. If you include any other file, this makes mixing versions of Chester impossible in some situations. The implementation file must include one of the d-pointer implementation variants. - -\section table Comparison Table - -The macros, classes, \#include statements above need to be replaced with different ones if you want one of the other kinds of d-pointers: - - - - - - - - - - -
TypePointer Declaration (1)Declaration Include (1) - Base Class(2, 3)Definition (2)Implementation Include (2)
Non-Shared, CopyableDECLARE_DPTR(Class)\#include \<\link dptr_base.h DPtrBase \endlink > - DPtrDEFINE_DPTR(Class) - \#include \<\link dptr.h DPtr \endlink > -
SharedDECLARE_SHARED_DPTR(Class)\#include \<\link dptr_base.h DPtrBase \endlink > - SharedDPtrDEFINE_SHARED_DPTR(Class) - \#include \<\link dptr_shared.h SharedDPtr \endlink > -
Non-CopyableDECLARE_NONCOPY_DPTR(Class)\#include \<\link dptr_base.h DPtrBase \endlink > - NonCopyDPtrDEFINE_NONCOPY_DPTR(Class) - \#include \<\link dptr_noncopy.h NonCopyDPtr \endlink > -
- -(1) The DECLARE_* macro and declartion include directive are supposed to be used in the header file of the main class. - -(2) The implementation include directive should only be used in the main class'es implementation file - use the DEFINE_* macro to create the glue code that connects the d-pointer class with the main class. - -(3) The base class must be used to derive the d-pointer class in the implementation file. It contains some functionality that the DEFINE_* macro relies on. -*/ +#ifndef DPTR_CLASS_0_1_H +#define DPTR_CLASS_0_1_H //hide the namespace /// \cond never diff --git a/dptr_base.h b/dptr_base.h index e53162d..85f2e52 100644 --- a/dptr_base.h +++ b/dptr_base.h @@ -23,11 +23,15 @@ \brief Expands to the fully qualified name of the d-pointer class. \param Class the fully qualified name of the class the d-pointer was declared in.*/ #define DPTR_CLASS_NAME(Class) Class::Private -///\def DPTR_NAME -///Expands to the local name of d-pointer classes (Private). + +/** \def DPTR_NAME +\brief Expands to the local name of d-pointer classes (Private). +*/ #define DPTR_NAME Private -///\def DPTR_WRAPPER_NAME -///Expands to the local name of the d-pointer wrapper class (DPrivate). + +/** \def DPTR_WRAPPER_NAME +Expands to the local name of the d-pointer wrapper class (DPrivate). +*/ #define DPTR_WRAPPER_NAME DPrivate #ifdef DECLARE_DPTR diff --git a/dptr_noncopy.h b/dptr_noncopy.h index fcff799..ed5ab7d 100644 --- a/dptr_noncopy.h +++ b/dptr_noncopy.h @@ -9,8 +9,8 @@ #include "dptr_base.h" -#ifndef DPTR_NONCOPY_CLASS_H -#define DPTR_NONCOPY_CLASS_H +#ifndef DPTR_NONCOPY_CLASS_0_1_H +#define DPTR_NONCOPY_CLASS_0_1_H //hide the namespace /// \cond never namespace Chester_0_1{ diff --git a/dptr_shared.h b/dptr_shared.h index 7e73853..b824194 100644 --- a/dptr_shared.h +++ b/dptr_shared.h @@ -9,8 +9,8 @@ #include "dptr_base.h" -#ifndef DPTR_SHAREDCLASS_H -#define DPTR_SHAREDCLASS_H +#ifndef DPTR_SHAREDCLASS_0_1_H +#define DPTR_SHAREDCLASS_0_1_H //hide the namespace /// \cond never namespace Chester_0_1{ diff --git a/tests/README b/tests/README new file mode 100644 index 0000000..a4cc97c --- /dev/null +++ b/tests/README @@ -0,0 +1,9 @@ +README for Tests +================= + +These are some very basic tests that check the functionality of Chester. +They have been written with Qt's QTest module, so you need Qt 4.x installed if +you want to use them. + +Simply call qmake and make to compile the tests, then call dptrtest to execute +the actual tests. diff --git a/tests/dptrpriv.cpp b/tests/dptrpriv.cpp index 06e4311..8242456 100644 --- a/tests/dptrpriv.cpp +++ b/tests/dptrpriv.cpp @@ -37,6 +37,12 @@ QString ClassWithSDptr::toString()const } int ClassWithSDptr::num()const{return d->num;} void ClassWithSDptr::setNum(int n){d->num=n;} +ClassWithSDptr ClassWithSDptr::clone()const +{ + ClassWithSDptr ret; + ret.d=d.clone(); + return ret; +} class DPTR_CLASS_NAME(ClassWithNDptr):public NonCopyDPtr { diff --git a/tests/dptrtest.cpp b/tests/dptrtest.cpp index 9fe6620..9151f7d 100644 --- a/tests/dptrtest.cpp +++ b/tests/dptrtest.cpp @@ -40,6 +40,12 @@ void DPtrTest::sharedDP() QCOMPARE(o1.num(),5); QCOMPARE(o2.num(),5); QCOMPARE(o3.num(),5); + //cloning + o2=o1.clone(); + o2.setNum(6); + QCOMPARE(o1.num(),5); + QCOMPARE(o2.num(),6); + QCOMPARE(o3.num(),5); } void DPtrTest::noncopyDP() diff --git a/tests/dptrtest.h b/tests/dptrtest.h index d95f523..67d4152 100644 --- a/tests/dptrtest.h +++ b/tests/dptrtest.h @@ -18,6 +18,7 @@ class ClassWithSDptr QString toString()const; int num()const; void setNum(int); + ClassWithSDptr clone()const; }; class ClassWithNDptr -- 1.7.2.5