Сообщение об ошибке, похоже, указывает на то, что это strlen
, прочитанное за буфером malloc
ed, выделенным strdup
. На 32-битной платформе оптимальная реализация strlen
может считывать по 4 байта за раз в 32-разрядный регистр и делать несколько бит-скрипов, чтобы увидеть, есть ли там нулевой байт. Если в конце строки осталось меньше 4 байтов, но 4 байта по-прежнему читаются для выполнения проверки нулевого байта, тогда я мог видеть, что эта ошибка печатается. В этом случае предположительно исполнитель strlen
будет знать, «безопасно» ли это сделать на конкретной платформе, и в этом случае ошибка valgrind является ложным положительным.
Подход 1 (Виртуальная функция)
Approach 2 (Class with Function Pointer)
Approach 3 (Class calling T functor)
FWIW, Указатели функций не являются тем же самым, что и Functors. Functors (на Си++) - это классы, которые используются для обеспечения вызова функции, которая обычно является operator().
Вот пример functor, а также шаблонная функция, которая использует аргумент functor:
class TFunctor
{
public:
void operator()(const char *charstring)
{
printf(charstring);
}
};
template<class T> void CallFunctor(T& functor_arg,const char *charstring)
{
functor_arg(charstring);
};
int main()
{
TFunctor foo;
CallFunctor(foo,"hello world\n");
}
С точки зрения производительности, Виртуальные функции и Указатели Функций приводят к косвенному вызову функции (т.е. через регистр), хотя виртуальные функции требуют дополнительной загрузки указателя VFTABLE перед загрузкой указателя на функцию. Использование Functors (с невиртуальным вызовом) в качестве обратного вызова является наиболее эффективным методом использования параметра для шаблонирования функций, поскольку они могут быть индуктированы и даже если не индуктированы, не генерируют непрямого вызова.
.Функциональные указатели больше в стиле Си, я бы сказал. В основном потому, что для их использования обычно необходимо определить плоскую функцию с той же самой сигнатурой, что и ваше определение указателя.
Когда я пишу C++, единственной плоской функцией, которую я пишу, является int main(). Все остальное является объектом класса. Из двух вариантов я бы выбрал определение класса и переопределил вашу виртуальную функцию, но если все, что вы хотите - это уведомить какой-нибудь код о том, что в вашем классе произошло какое-то действие, то ни один из этих вариантов не будет лучшим решением.
Я не знаю вашей точной ситуации, но вы можете захотеть просмотреть шаблоны дизайна
Я бы предложил наблюдательный шаблон. Это то, что я использую, когда мне нужно следить за классом или ждать какого-нибудь уведомления.
iFunc
не может быть NULL, вы не используете пустоту *iParam
, etcВероятно, лучший способ сделать это, когда это возможно. Он будет иметь наилучшую производительность, он будет безопасен при вводе текста, и его легко понять (это метод, используемый в STL)
.Из вашего примера не ясно, создаете вы класс утилиты или нет. Вы Callback класс предназначен для реализации закрытия или более существенного объекта, который Вы просто не дополнили? Первая форма:
Вторая форма:
В конечном итоге, IMO, первая форма лучше подходит для всех обычных случаев. Вторая имеет некоторые интересные возможности, хотя -- но не те, которые вам понадобятся часто.
.Одним из главных преимуществ первого метода является большая безопасность типа. Второй метод использует пустоту * для iParam, поэтому компилятор не сможет диагностировать проблемы с типами.
Незначительным преимуществом второго метода является то, что было бы меньше работы по интеграции с Си. Но если в основе кода только Си++, то это преимущество является спорным.
.Например, давайте посмотрим на интерфейс для добавления read функциональности к классу:
struct Read_Via_Inheritance
{
virtual void read_members(void) = 0;
};
Всякий раз, когда я хочу добавить другой источник чтения, я должен унаследовать от класса и добавить конкретный метод:
struct Read_Inherited_From_Cin
: public Read_Via_Inheritance
{
void read_members(void)
{
cin >> member;
}
};
Если я хочу читать из файла, базы данных или USB, это требует еще 3 отдельных класса. Комбинации начинают быть очень уродливыми с множеством объектов и множеством источников.
Если я использую functor, который случайно напоминает шаблон проектирования Visitor:
struct Reader_Visitor_Interface
{
virtual void read(unsigned int& member) = 0;
virtual void read(std::string& member) = 0;
};
struct Read_Client
{
void read_members(Reader_Interface & reader)
{
reader.read(x);
reader.read(text);
return;
}
unsigned int x;
std::string& text;
};
С вышеуказанным фундаментом, объекты могут читаться из разных источников, просто снабжая разных читателей методом read_member
:
struct Read_From_Cin
: Reader_Visitor_Interface
{
void read(unsigned int& value)
{
cin>>value;
}
void read(std::string& value)
{
getline(cin, value);
}
};
Мне не нужно менять код объекта (это хорошо, потому что он уже работает). Я также могу применить читатель к другим объектам.
Обычно я использую наследование, когда выполняю общее программирование . Например, если у меня есть класс Field
, то я могу создать Field_Boolean
, Field_Text
и Field_Integer
. In может поместить указатели на свои экземпляры в вектор
и назвать его записью. Запись может выполнять общие операции с полями, и не заботится или не знает, что вид поля обрабатывается.