Здравствуйте, FDSC, Вы писали:
FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
+ сделать публичный интерфейс, а доступ к внутреннему классу I закрыть (а то так можно создать экземпляр итератора без коллекции, это обходится, но зачем добавлять костыли?).
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, alzt, Вы писали:
A>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>Остальные знают только об интерфейсе итератора.
FDS>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках.
Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
FDS>>Здравствуйте, alzt, Вы писали:
A>>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>>Остальные знают только об интерфейсе итератора.
FDS>>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
A>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках. A>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
Здравствуйте, igna, Вы писали:
I>Спасибо, но и с этими изменениями не компилируется. Если можно дай компилируемый вариант.
class O
{
};
class I;
class C
{
public:
I GetIterator();
private:
vector<O> m_elems;
int Count();
O GetElem(int index);
friend class I;
};
class I
{
public:
virtual void Reset()=0;
virtual bool IsNext()=0;
virtual O Current()=0;
protected:
int Count(){return c->Count();}
O GetElem(int index){return c->GetElem(index);}
private:
C* c;
};
class I1 : I
{
public:
I1():m_index(0){}
void Reset(){m_index = 0;}
bool IsNext(){return m_index<I::Count();}
O Current(){return I::GetElem(m_index);}
private:
int m_index;
};
Здравствуйте, FDSC, Вы писали:
A>>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках. A>>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
FDS>С какой стати его придётся копировать?
Объект класса C предположим имеет скрытую переменную m_index.
Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо.
Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
A>>>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках. A>>>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
FDS>>С какой стати его придётся копировать?
A>Объект класса C предположим имеет скрытую переменную m_index. A>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо. A>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?
Здравствуйте, FDSC, Вы писали:
A>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>Остальные знают только об интерфейсе итератора.
FDS>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались.
Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода.
Если же перенести функциональность класса I в класс C, то менять будет сложнее.
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
A>>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>>Остальные знают только об интерфейсе итератора.
FDS>>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
A>А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались. A>Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода. A>Если же перенести функциональность класса I в класс C, то менять будет сложнее.
Нет.
1. Можно сделать наследников класса C
или
2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort
То же можно задать и для фильтра.
Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
Здравствуйте, FDSC, Вы писали:
A>>А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались. A>>Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода. A>>Если же перенести функциональность класса I в класс C, то менять будет сложнее.
FDS>Нет. FDS>1. Можно сделать наследников класса C
Вариант плохой, т.к. при наличии других особенностей (не только тип обхода менять надо, а например и тип самого объекта) количество наследников резко увеличивается.
Пример: 2 обхода — прямой, реверсивный и 2 типа ОС — виндоус, юникс дают уже 4 наследника. 3 обхода, 3 типа ОС — 9.
FDS>или FDS>2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort FDS>То же можно задать и для фильтра.
FDS>Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
Достаточно. Это решение намного проще и единственно верное?
Можно и шаблоны использовать, но количество деталей, которые надо будет держать в голове при этом не уменьшится.
Здравствуйте, FDSC, Вы писали:
A>>Объект класса C предположим имеет скрытую переменную m_index. A>>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо. A>>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
FDS>Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?
На мой взгяд это будет сложнее. Можно также простейший пример реализации?
"ComeauTest.c", line 14: error: function returning abstract class "I" is not
allowed:
function "I::Reset" is a pure virtual function
function "I::IsNext" is a pure virtual function
function "I::Current" is a pure virtual function
I GetIterator();
^
I>"ComeauTest.c", line 14: error: function returning abstract class "I" is not
I> allowed:
I> function "I::Reset" is a pure virtual function
I> function "I::IsNext" is a pure virtual function
I> function "I::Current" is a pure virtual function
I> I GetIterator();
I> ^
I>
Здравствуйте, alzt, Вы писали:
FDS>>или FDS>>2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort FDS>>То же можно задать и для фильтра.
FDS>>Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
A>Достаточно. Это решение намного проще и единственно верное? A>Можно и шаблоны использовать, но количество деталей, которые надо будет держать в голове при этом не уменьшится.
Это решение проще с той точки зрения, что вместо нового класса итератора и новой функции в C мы будем писать только вспомогательную функцию. Так что это решение чем-то проще — не нужно изменять класс C
По крайней мере, оно и не сложней, тем более, что над C можно сделать такие же как и у вас обёртки I, которые уже ничего не будут знать о приватном интерфейсе C
Если заменить возвращаемое значение на указатель, то можно то же самое написать без использования friend, если определить I и I1 внутри C:
#include <vector>
using namespace std;
class O
{
};
class I;
class C
{
class I
{
public:
virtual void Reset()=0;
virtual bool IsNext()=0;
virtual O Current()=0;
protected:
int Count(){return c->Count();}
O GetElem(int index){return c->GetElem(index);}
private:
C* c;
};
class I1 : I
{
public:
I1():m_index(0){}
void Reset(){m_index = 0;}
bool IsNext(){return m_index<I::Count();}
O Current(){return I::GetElem(m_index);}
private:
int m_index;
};
public:
I* GetIterator();
private:
vector<O> m_elems;
int Count();
O GetElem(int index);
};
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
A>>>Объект класса C предположим имеет скрытую переменную m_index. A>>>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо. A>>>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
FDS>>Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?
A>На мой взгяд это будет сложнее. Можно также простейший пример реализации?
Это на C# 2.0:
using System;
using System.Collections.Generic;
using System.Text;
namespace FriendDown
{
public class C<T>
{
public class SearchCtx
{
private SearchFilter _SF;
private SequenceFunc _CF;
private int _Number;
public SearchCtx(SearchFilter SF, SequenceFunc CF)
{
ResetCounter();
_SF = SF;
_CF = CF;
}
private SearchCtx()
{
throw new System.NotSupportedException();
}
public void ResetCounter()
{
_Number = 0;
}
public int Number
{
get
{
return _Number;
}
}
public int ToNext
{
get
{
return _Number++;
}
}
public SequenceFunc CF
{
get
{
return _CF;
}
}
public SearchFilter SF
{
get
{
return _SF;
}
}
}
private List<T> _ObjectList = new List<T>();
public delegate bool SearchFilter(T sender);
public delegate int SequenceFunc(int Number, int Count, object O);
public bool Next(SearchCtx SC, object SearchParams, out T t)
{
bool isFound = false;
do
{
int i = SC.CF(SC.ToNext, _ObjectList.Count, SearchParams);
if (i >= _ObjectList.Count || i < 0)
{
t = default(T);
return false;
}
t = _ObjectList[i];
isFound = SC.SF(t);
}
while (!isFound);
return true;
}
public void Add(T addT)
{
_ObjectList.Add(addT);
}
private object LockObj = new Object();
public SearchCtx GetSearch(SearchFilter SF, SequenceFunc CF)
{
return new SearchCtx(SF, CF);
}
}
static class Filter2
{
public static bool SF(int sender)
{
return (sender & 1) == 0 ? true : false;
}
public static int CF(int Number, int Count, object O)
{
return Number;
}
}
class Program
{
static void Main(string[] args)
{
C<int> c = new C<int>();
bool isEndInput = false;
do
{
int i;
isEndInput = !Int32.TryParse(Console.ReadLine(), out i);
if (!isEndInput)
c.Add(i);
}
while (!isEndInput);
C<int>.SearchCtx ctx1 = c.GetSearch(delegate(int sender) {return true;},
delegate(int Number, int Count, object O) { return Count - Number - 1; }
);
C<int>.SearchCtx ctx2 = c.GetSearch(Filter2.SF, Filter2.CF);
int val1, val2;
while (c.Next(ctx1, null, out val1))
{
while (c.Next(ctx2, null, out val2))
{
Console.WriteLine("ctx2: " + val2.ToString());
}
ctx2.ResetCounter();
Console.WriteLine("ctx1: " + val1.ToString());
}
Console.ReadLine();
}
}
}
Здравствуйте, igna, Вы писали:
I>Если заменить возвращаемое значение на указатель, то можно то же самое написать без использования friend, если определить I и I1 внутри C:
Можно и так, сразу не пришло в голову.
Только тогда обернуть лучше указатель.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, alzt, Вы писали:
A>>VS2005 отлично компилирует
I>Да, но вроде не должен, поскольку GetIterator пытается вернуть объект абстрактного класса I. Давай заменим на I* GetIterator()?
Понял почему он компилируется: GetIterator() я не определяю, а насчёт класса I — в момент определения класса C о нём ничего не известно (что он абстрактный). Надо указатель возвращать.
Проверить пока не могу — а в C# подобный пример скомпилируется?