Мы используем расстояние Левенштейна метод для проверки на дублирующихся клиентов в нашей базе данных. Это работает вполне хорошо.
Я помню, как читал краткое описание идиомы публично-не-виртуальный / непублично-виртуальный и ее преимуществ, но не где. У этой викибука хорошее описание.
Вот как вы применяете его к op ==:
struct A {
virtual ~A() {}
int a;
friend
bool operator==(A const& lhs, A const& rhs) {
return lhs.equal_to(rhs);
}
// http://en.wikipedia.org/wiki/Barton-Nackman_trick
// used in a simplified form here
protected:
virtual bool equal_to(A const& other) const {
return a == other.a;
}
};
struct B : A {
int b;
protected:
virtual bool equal_to(A const& other) const {
if (B const* p = dynamic_cast<B const*>(&other)) {
return A::equal_to(other) && b == p->b;
}
else {
return false;
}
}
};
struct C : A {
int c;
protected:
virtual bool equal_to(A const& other) const {
if (C const* p = dynamic_cast<C const*>(&other)) {
return A::equal_to(other) && c == p->c;
}
else {
return false;
}
}
};
Один из способов сделать это - использовать виртуальный оператор ==
, который принимает объект базового класса в качестве параметра, чтобы он правильно работал с различными производными объектами. Однако вам нужно сделать эту функцию чисто виртуальной, чтобы заставить все производные объекты реализовать ее. Таким образом, вы не сможете создать экземпляр базового класса. Например:
class A
{
public:
virtual ~A(){}
//A virtual operator for comparison
virtual bool operator==(const A& a) = 0;
protected:
bool compareBase(const A& a);
private:
int m_data;
};
bool A::compareBase(const A &a)
{
return m_data == a.m_data;
}
class B1 : public A
{
public:
//Override the base class
bool operator==(const A& a);
private:
bool compare(const B1* pB)
{
if(compareBase(*pB))
{
//Code for compare
return true;
}
return false;
}
};
bool B1::operator ==(const A &a)
{
//Make sure that the passed type is same
const B1* pB = dynamic_cast<const B1*>(&a);
if(pB )
{
return compare(pB);
}
return false;
}
//Similarly implement for B2
Могут ли разные производные классы создавать одинаковые объекты?
Если так: двойная отправка - это вариант: он требует перегрузки в базовом классе , поэтому у вас будут зависимости
В противном случае: решение находится в операторе == (), чтобы проверить typeid и вернуть false, если они разные. В противном случае вызовите частную функцию equal (), в которой производный класс может выполнять static_cast и сравнивать.
Если вы не возражаете, что базовый класс ссылается на подклассы, тогда двойная отправка:
#include <iostream>
class B;
class C;
class A
{
public:
int data;
virtual bool equals (const A* rhs) const
{
std::cout << " A==A ";
return data == rhs->data;
}
virtual bool equals (const B* rhs) const {return false;}
virtual bool equals (const C* rhs) const {return false;}
};
class B : public A
{
public:
float some_data;
virtual bool equals (const A* rhs) const
{
return rhs->equals (this);
}
virtual bool equals (const B* rhs) const
{
std::cout << " B==B ";
return A::equals (static_cast<const A*> (rhs)) && some_data == rhs->some_data;
}
};
class C : public A
{
public:
double more_data;
virtual bool equals (const A* rhs) const
{
return rhs->equals (this);
}
virtual bool equals (const C* rhs) const
{
std::cout << " C==C ";
return A::equals (static_cast<const A*> (rhs)) && more_data == rhs->more_data;
}
};
bool operator== (const A& lhs, const A& rhs)
{
return lhs.equals (&rhs);
}
int main (int argc, char* argv[])
{
A* one = new B;
A* two = new B;
A* three = new C;
std::cout << (*one == *one) << std::endl;
std::cout << (*one == *two) << std::endl;
std::cout << (*one == *three) << std::endl;
std::cout << (*three == *three) << std::endl;
return 0;
}
Делает это без необходимости dynamic_cast.
Не беспокойтесь. Тетя Гугл нашла его для меня
chflags -R nouchg .
Из комментариев здесь :
Если вы меняете рабочие области в OS X и вы импортируете проект на основе SVN в ваше новое рабочее пространство, некоторые из ваших файлы могут иметь установленный флаг uchg. SubClipse / SVN не сможет обновить этот проект. Вы получите ошибка:
svn: не удается переименовать файл
каждый раз, когда вы пытаетесь вызвать svn. если ты проблема:
chflags -R nouchg.
на верхнем уровне проекта каталог, это очистит эти флаги
В противном случае вам нужно реализовать некоторые компараторы с большим количеством dynamic_cast <> --ing, чтобы действительно делать это правильно. В качестве альтернативы вы можете реализовать функцию для создания хэш-кода для каждого объекта и использовать его, например
class A { public: int data; virtual long getHashCode() const { // compute something here for object type A } // virtual here just in case you need to overload it in B or C virtual bool equals( const A& obj ) const { return (typeid(*this) == typeid(obj) && getHashCode() == obj->getHashCode()); } }; class B : public A { public: float more_data; bool something_else; virtual long getHashCode() const { // compute something here for object type B } }; class C : public A { public: double more_data; virtual long getHashCode() const { // compute something here for object type C } };
. Если вы каким-то образом включаете тип объекта в хэш-код (не показано выше), вы также можете обойтись без глупого typeid ( ) сравнения выше.