patterncppMinor
Visitor pattern in C++
Viewed 0 times
visitorpatternstackoverflow
Problem
I have just implemented a quick test of the visitor design pattern in C++ and have a few questions regarding the code.
main.cpp
IVisitable.h
Base.h
Base.cpp
Derived.h
Derived.cpp
Visitor.h
Visitor.cpp
And here are some questions, of course I am interested in all remarks you may have not just answers to these.
-
How do you keep track of includes? Even in this short example I now have includes that are not needed anymore.
-
Is this
-
Is there any way to treat an object as a superclass without a pointer? I.e. can I do something like this:
-
When can I use a forward declaration like
main.cpp
#include
#include "Base.h"
#include "Derived.h"
#include "Visitor.h"
int main() {
Base base;
std::cout acceptvisitor(v);
}IVisitable.h
#ifndef IVISITABLE_H
#define IVISITABLE_H
class Visitor;
class IVisitable {
virtual void acceptvisitor(Visitor v) = 0;
};
#endifBase.h
#ifndef BASE_H
#define BASE_H
#include
#include "IVisitable.h"
class Base : public IVisitable {
public:
void acceptvisitor(Visitor v);
};
#endifBase.cpp
#include "Base.h"
#include "Visitor.h"
void Base::acceptvisitor(Visitor v) {
v.visit(*this);
}Derived.h
#include "Base.h"
class Derived : public Base {
public:
void acceptvisitor(Visitor v);
};Derived.cpp
#include "Derived.h"
#include "Visitor.h"
void Derived::acceptvisitor(Visitor v) {
v.visit(*this);
}Visitor.h
#ifndef VISITOR_H
#define VISITOR_H
class Base;
class Derived;
class Visitor {
public:
void visit(Base b);
void visit(Derived d);
};
#endifVisitor.cpp
#include "Visitor.h"
#include
#include "Base.h"
#include "Derived.h"
void Visitor::visit(Base b) {
std::cout << "Visiting Base\n";
}
void Visitor::visit(Derived d) {
std::cout << "Visiting Derived\n";
}And here are some questions, of course I am interested in all remarks you may have not just answers to these.
-
How do you keep track of includes? Even in this short example I now have includes that are not needed anymore.
-
Is this
#ifndef treatment of header files still normal? I picked it up somewhere but have never actually seen anybody else do it.-
Is there any way to treat an object as a superclass without a pointer? I.e. can I do something like this:
Base d = Derived();-
When can I use a forward declaration like
class Visitor; and when do I need to include the actual header file? Or when shouldSolution
Well your code is all broken.
But we will get to that as we answer your questions:
Answers to Questions
How do you keep track of includes? Even in this short example I now have includes that are not needed anymore.
Using this rule it is easy to only include files you need. In your header files only include other header files if you need them. You only need them if the object you are defining has a member of that type or is derived from that type (or takes a parameter to a method by value).
In all other situations forward declare.
This reduces the header count considerably.
In the source file you include all the header files of object you use to implement your code (that your header files have not included).
Is this #ifndef treatment of header files still normal? I picked it up somewhere but have never actually seen anybody else do it.
Yes you should always place include guards around header files to protect from multiple inclusion.
Is there any way to treat an object as a superclass without a pointer? I.e. can I do something like this: Base d = Derived();
Well actually what you have written will compile:
Unfortunately it does not do what you expect. Here you have object slicing. The Base part of the object you created is sliced out and copied into d. What you need is a pointer (or a reference).
It looks like you are used to languages like Java. Where all objects are dynamically allocated. C++ has a much superior mechanism that allows us to accurately control the lifetime of the object. The disadvantage is that it adds complexity to the language.
What is called a pointer in C/C++ (to distinguish it from C++ references) would in most other languages be called a reference. C++ has both local objects (automatic storage duration object) and dynamically allocated objects (dynamic storage duration objects) thus we need a convention that allows access to both types of object, hence we use
BUT it is unusual to use pointers directly in C++ (unless you are implementing some real low level stuff). Most of the time when you dynamically allocate objects you will use a smart pointer that defines the lifespan of the object (much like other languages with garbage collection (but better)).
When can I use a forward declaration like "class Visitor;" and when do I need to include the actual header file. Or when should I?
As described above.
In the header file (were there is only declaration) only include another header if the header file defines a type that is a member or is used as a parent or used to pass a parameter by value (parameters are infrequent as they are normally passed by reference). In all other cases in a header file you should use forward declaration. In the source file include the header files that define types that you use (ie call methods on).
Note: You only need a forward declaration for objects that are pointers or references.
Should I make implementations of abstract functions virtual? It seems not to matter.
Only virtual methods can be abstract.
If you forget to define a method the compiler will not complain (as you may define it in another compilation unit). The linker will only complain if somebody tries to call the method and can't find a definition. So if you don't call it then there will be no error (but if you don't call it then it does not matter).
When you implement a virtual function in a derived class it is probably best to mark it as virtual to show a su
But we will get to that as we answer your questions:
Answers to Questions
How do you keep track of includes? Even in this short example I now have includes that are not needed anymore.
- Only include a header file if you need the class definition.
- Forward declare everything else
Using this rule it is easy to only include files you need. In your header files only include other header files if you need them. You only need them if the object you are defining has a member of that type or is derived from that type (or takes a parameter to a method by value).
In all other situations forward declare.
This reduces the header count considerably.
In the source file you include all the header files of object you use to implement your code (that your header files have not included).
#ifndef BASE_H
#define BASE_H
#include // There is no need for this.
// There are no members that use string
// You are not deriving from string (who would)
// You are not passing a string as a parameter
// Remove this.
#include "IVisitable.h"
class Base : public IVisitable {
public:
void acceptvisitor(Visitor v); // By the way this is probably wrong.
// You are passing by value and thus will
// Make a copy.
// Here you (if you really
// wanted to pass by value) you should have
// include "Visitor.h".
//
// But you don't want to pass by value you want
// to pass by reference. So you just need to
// forward declare.
};
#endifIs this #ifndef treatment of header files still normal? I picked it up somewhere but have never actually seen anybody else do it.
Yes you should always place include guards around header files to protect from multiple inclusion.
Is there any way to treat an object as a superclass without a pointer? I.e. can I do something like this: Base d = Derived();
Well actually what you have written will compile:
Unfortunately it does not do what you expect. Here you have object slicing. The Base part of the object you created is sliced out and copied into d. What you need is a pointer (or a reference).
It looks like you are used to languages like Java. Where all objects are dynamically allocated. C++ has a much superior mechanism that allows us to accurately control the lifetime of the object. The disadvantage is that it adds complexity to the language.
What is called a pointer in C/C++ (to distinguish it from C++ references) would in most other languages be called a reference. C++ has both local objects (automatic storage duration object) and dynamically allocated objects (dynamic storage duration objects) thus we need a convention that allows access to both types of object, hence we use
* to refer to objects that are dynamically allocated (to distinguish them from local objects).BUT it is unusual to use pointers directly in C++ (unless you are implementing some real low level stuff). Most of the time when you dynamically allocate objects you will use a smart pointer that defines the lifespan of the object (much like other languages with garbage collection (but better)).
std::shared_ptr would be the equivalent of T in Java.std::shared_ptr d = new Derived(); // dynamically allocated object
// That will be correctly destroyed
// when there are no more references.When can I use a forward declaration like "class Visitor;" and when do I need to include the actual header file. Or when should I?
As described above.
In the header file (were there is only declaration) only include another header if the header file defines a type that is a member or is used as a parent or used to pass a parameter by value (parameters are infrequent as they are normally passed by reference). In all other cases in a header file you should use forward declaration. In the source file include the header files that define types that you use (ie call methods on).
Note: You only need a forward declaration for objects that are pointers or references.
Should I make implementations of abstract functions virtual? It seems not to matter.
Only virtual methods can be abstract.
If you forget to define a method the compiler will not complain (as you may define it in another compilation unit). The linker will only complain if somebody tries to call the method and can't find a definition. So if you don't call it then there will be no error (but if you don't call it then it does not matter).
When you implement a virtual function in a derived class it is probably best to mark it as virtual to show a su
Code Snippets
#ifndef BASE_H
#define BASE_H
#include <string> // There is no need for this.
// There are no members that use string
// You are not deriving from string (who would)
// You are not passing a string as a parameter
// Remove this.
#include "IVisitable.h"
class Base : public IVisitable {
public:
void acceptvisitor(Visitor v); // By the way this is probably wrong.
// You are passing by value and thus will
// Make a copy.
// Here you (if you really
// wanted to pass by value) you should have
// include "Visitor.h".
//
// But you don't want to pass by value you want
// to pass by reference. So you just need to
// forward declare.
};
#endifstd::shared_ptr<Base> d = new Derived(); // dynamically allocated object
// That will be correctly destroyed
// when there are no more references.class IVisitable {
virtual void acceptvisitor(Visitor& v) = 0;
}; // ^^^ pass by referenceContext
StackExchange Code Review Q#13660, answer score: 9
Revisions (0)
No revisions yet.