简体   繁体   中英

C++ Interfaces in stl::list

LessonInterface

class ILesson
{
    public:
        virtual void PrintLessonName() = 0;
        virtual ~ILesson() {}
};

stl container

typedef list<ILesson> TLessonList;

calling code

for (TLessonList::const_iterator i = lessons.begin(); i != lessons.end(); i++)
    {
        i->PrintLessonName();
    }

The error:

Description Resource Path Location Type passing 'const ILesson' as 'this' argument of 'virtual void ILesson::PrintLessonName()' discards qualifiers

PrintLessonName must be declared as const to be able to be called on const ILessons. Otherwise the compiler assumes it may modify the ILesson and prevents the call.

virtual void PrintLessonName() const = 0;

You can't "put" objects of a class that has pure virtual functions(because you can't instantiate it). Maybe you mean:

// store a pointer which points to a child actually.
typedef list<ILesson*> TLessonList;

OK, as others pointed out, you have to make PrintLessonName a const member function. I would add that there is another small pitfall here. PrintLessonName must be const in both the base and the derived classes, otherwise they will not have the same signature:

class ILesson
{
public:
    virtual void PrintLessonName() const = 0;
    virtual ~ILesson() {}
};


class SomeLesson : public ILesson
{
public:
    // const is mandatory in the child
    virtual void PrintLessonName() const
    {
        //
    }
    virtual ~SomeLesson() {}
};

To be honest, I find Jerry Coffin's answer helpful for redesigning the printing functionality.

使用iterator而不是const_iterator或make PrintLessonName() const函数:

virtual void PrintLessonName() const = 0

You have to make PrinLessonName const.

virtual void PrintLessonName() const = 0;

Or not use a const_iterator, of course.

You want a list of pointers to ILesson's.

IMO, you'd also be considerably better off adding something like:

std::ostream &operator<<(std::ostream &os, ILesson const *il) { 
    il->PrintLessonName(os);
    return os;
}

Then, instead of the loop you've written above, you can use something like:

std::copy(lessons.begin(), lessons.end(), 
          std::ostream_iterator<ILesson *>(std::cout));

As you can see, I've added one other minor embellishment in the process -- PrintLessonName takes a stream as its argument, instead of always printing to the same place. Of course, if you're not using streams, you may not want that...

Edit: Of course the other comments that you want to make PrintLessonPlan const are also correct...

You call a non-const method for a const object refered through a reference to const object.

Anyways:

I'm 100% sure you need to have a list of pointers:

typedef list<ILesson*> TLessonList;

in order to take advantage of polymorphism.

Having a list of values of ILesson is not possible, since ILesson is an abstract class.

Don't forget to delete the objects in the list of pointers, to avoid memory leaks.

A version like this:

for (TLessonList::const_iterator i=lessons.begin(), m=lessons.end();  i!=m;  ++i)
    {
        i->PrintLessonName();
    }

lessons.end() gets called once, and also note ++i instead of i++, which is faster (the post-increment operator involves creation of a temporary object, while the pre-increment doesn't).

People are correct about the lack of const. I'd favour using the for_each algorithm this will prevent calling lessons.end() for every entry.

#include <algorithm> //for for_each()

Then use this:

std::for_each(  lessons.begin(), lessons.end(), std::mem_fun(&ILesson::PrintLessonName) )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM