Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
programming:cpp-style-tips [2008/01/30 07:37]
127.0.0.1 external edit
programming:cpp-style-tips [2013/09/19 16:41] (current)
Line 5: Line 5:
  
 It is a very good thing to make a modular architecture. It permits creating several implementation for one functionnality (several ways to do it), and to easily switch from one to the other, or change a part of the program. It is a very good thing to make a modular architecture. It permits creating several implementation for one functionnality (several ways to do it), and to easily switch from one to the other, or change a part of the program.
 +
 +==== Inheritance ====
  
 The most obvious way to do that is inheritance. You create an interface, which defines what your implementation must be able to do, and you inheritate several implementations that do it with different ways. Then anywhere else you always use the interface class, and it will use the implementation you initialized with, no matter what it is. The most obvious way to do that is inheritance. You create an interface, which defines what your implementation must be able to do, and you inheritate several implementations that do it with different ways. Then anywhere else you always use the interface class, and it will use the implementation you initialized with, no matter what it is.
Line 56: Line 58:
 </code> </code>
  
-It does the job, but it is not the best way, because all functions must be virtual, so they cannot be inlined. +It does the job, but it is not the best way, because all functions must be virtual, so they cannot be inlined and the code will be very slow if functions are called often.
  
 Moreover this does not really respect the philosophy of inheritance. Inheritance represents the relationship "is a kind of". But ''ColorClassRGB'' is not a kind of ''ColorClass'', it is a ''ColorClass'' implementation.  Moreover this does not really respect the philosophy of inheritance. Inheritance represents the relationship "is a kind of". But ''ColorClassRGB'' is not a kind of ''ColorClass'', it is a ''ColorClass'' implementation. 
  
 Eventually, it does not always work. If you create the ''Animal'' class with a virtual ''clone'' function (that returns an ''Animal'' of course), and a ''Cow'' class which inherits from ''Animal'', you would like that the ''clone'' function of ''Cow'' return a ''Cow'', but you can't (overriden functions must have same signature). Thus your ''clone'' function must return an ''Animal'', and if you want to use it as a ''Cow'' (because you know it is a ''Cow''), you have to cast-down to a ''Cow'', which is painful and absolutely not secure (possible errors at execution time). Eventually, it does not always work. If you create the ''Animal'' class with a virtual ''clone'' function (that returns an ''Animal'' of course), and a ''Cow'' class which inherits from ''Animal'', you would like that the ''clone'' function of ''Cow'' return a ''Cow'', but you can't (overriden functions must have same signature). Thus your ''clone'' function must return an ''Animal'', and if you want to use it as a ''Cow'' (because you know it is a ''Cow''), you have to cast-down to a ''Cow'', which is painful and absolutely not secure (possible errors at execution time).
 +
 +==== Templates ====
  
 There is another solution, which consists in using templates. Give the ''ColorClass'' as a template parameter. The use of this class will define what ''ColorClass'' must do (if something is missing, compilation will fail). \\ There is another solution, which consists in using templates. Give the ''ColorClass'' as a template parameter. The use of this class will define what ''ColorClass'' must do (if something is missing, compilation will fail). \\
Line 133: Line 137:
 </code> </code>
  
-It works. But you have to be sure that your counter is signed, and it won't work in some other cases, if your counter is not just an integer but a pointer scanning   a list for example. It's ugly.+It works. But it won't work in some other cases, if your counter is not just an integer but a pointer scanning a list for example. It's ugly.
  
 A nicer workaround : A nicer workaround :
Line 243: Line 247:
 </code> </code>
  
 +===== Copy of abstract objects =====
 +
 +==== Clone function ====
 +
 +You have an abstract class, and several implementations of this class. You have a pointer to the abstract class, but you don't know which implementations it is, and you want to make a copy of this object. The solution is to use a ''clone'' function.
 +
 +<code cpp>
 +class Descriptor
 +{
 + public:
 +  virtual Descriptor* clone() = 0;
 +};
 +
 +class Type1Descriptor: public Descriptor
 +{
 + public:
 +  virtual Descriptor* clone()
 +  {
 +    return new Type1Descriptor(*this);
 +  }
 +};
 +
 +class AnotherClass
 +{
 +  Descriptor *my_descriptor;
 + public:
 +  AnotherClass(Descriptor *modelDescriptor)
 +  {
 +    my_descriptor = modelDescriptor->clone();
 +  }
 +};
 +</code>
 +
 +==== Factory model ====
 +
 +If you just want to copy the configuration parameters of the class, you can create a Factory class that will have the same constructor parameters than your class and will store them internally, and that has a ''create'' function that will create a copy of the class with these parameters.
 +
 +<code cpp>
 +class Descriptor
 +{
 + public:
 +  virtual void describe() = 0;
 +  Descriptor(int param1, int param2): param1(param1), param2(param2) { internalData = 0; }
 +};
 +
 +class DescriptorFactory
 +{
 + public:
 +  virtual Descriptor* create() const = 0;
 +}
 +
 +class Type1Descriptor: public Descriptor
 +{
 +  int param1;
 +  int param2;
 +  int internalData;
 + public:
 +  Descriptor(int param1, int param2): param1(param1), param2(param2) { internalData = 0; }
 +  virtual void describe() { ... }
 +};
 +
 +class Type1DescriptorFactory: public DescriptorFactory
 +{
 +  int param1;
 +  int param2;
 + public:
 +  Descriptor(int param1, int param2): param1(param1), param2(param2) {}
 +  virtual Descriptor* create() const
 +  {
 +    return new Type1Descriptor(param1, param2);
 +  }
 +}
 +
 +
 +class AnotherClass
 +{
 +  Descriptor *my_descriptor;
 + public:
 +  AnotherClass(DescriptorFactory *descriptorFactory)
 +  {
 +    my_descriptor = descriptorFactory->create();
 +  }
 +};
 +</code>
 +
 +===== Inherited parameters =====
 +
 +
 +First see [[cpp-style-tips#Modular architecture]] to see why you should maybe use templates instead of inheritance here.
 +
 +You want two descriptors of same type being able to merge. So you have to declare it in the base class, with the base type, in order to be able to use it in general. But you don't know how to merge two different kinds of descriptors. If you implement a "generic merger" (with base class as parameter) that does nothing, it will be used in priority, even if you overloaded it with inherited class. The only solution is dynamic casting.
 +
 +<code cpp>
 +class Descriptor
 +{
 + public:
 +  virtual void merge(Descriptor* desc) = 0;
 +};
 +
 +class Type1Descriptor
 +{
 + public:
 +  virtual void merge(Descriptor* desc)
 +  {
 +    Type1Descriptor *desc1 = dynamic_cast<Type1Descriptor>(desc);
 +    if (desc1 == NULL)
 +    {
 +      std::cout << "Error: cannot merge Type1Descriptor with another type of Descriptor" << std::endl;
 +      return;
 +    }
 +    // do the merging of two Type1Descriptor here
 +  }
 +};
 +</code>
  
 ====== C style tips ====== ====== C style tips ======
programming/cpp-style-tips.1201678678.txt.gz ยท Last modified: 2013/09/19 16:43 (external edit)
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0