Это не касается Вашего данного примера, но так как Вы упомянули, что действительно интересуетесь shared_ptr
поведение при удалении его находящегося в собственности объекта, Вы могли бы интересоваться использованием shared_ptr
'средство удаления'.
, Если объект, принадлежавший shared_ptr
потребности специальная обработка, будучи удаленным, можно определить 'средство удаления' для какого-либо конкретного shared_ptr<>
. Средство удаления не является частью типа, это - атрибут shared_ptr<>
экземпляр, таким образом, Ваш контейнер shared_ptr<>
объекты мог иметь некоторые объекты с различными средствами удаления. Вот то, что документы Повышения говорят о shared_ptr<>
средство удаления:
Пользовательские deallocators позволяют функции фабрики возврат
shared_ptr
для изоляции пользователя от его стратегии выделения памяти. Так как deallocator не является частью типа, изменение стратегии выделения не повреждает источник или совместимость на уровне двоичных кодов, и не требует клиентской перекомпиляции. Например, "нет" deallocator полезен при возвратеshared_ptr
к статически выделенному объекту, и другие изменения позволяютshared_ptr
использоваться в качестве обертки для другого интеллектуального указателя, упрощая совместимость.
Это было бы самым чистым, если Вы могли бы изменить IFoo
для имения виртуального деструктора, так как Вы планируете удалить объекты, которые являются подклассами его через IFoo
ссылка или указатель. Но если Вы застреваете с IFoo
, который не может быть исправлен, тогда если Вы хотите использовать shared_ptr<IFoo>
в Вашем контейнере, но иметь его указывающий Bar
, Вы могли создать shared_ptr
экземпляр со средством удаления, которое работает, удрученное к Bar*
тогда делает удалить операцию. Downcasts считают невоспитанностью, но эта техника могла бы быть чем-то, что Вы могли использовать в связывании.
Я не знаю, как уродливо выглядит следующее, но все равно публикую :-)
(Основная идея состоит в том, чтобы зарегистрировать указатели функций, например, что atexit функция выполняет.
Конечно, реализация atexit
отличается)
В основном модуле у нас может быть что-то вроде этого:
typedef int (*function_t)(void);
static function_t vfunctions[100]; // we can store max 100 function pointers
static int vcnt = 0; // count the registered function pointers
int add2init(function_t f)
{
// todo: error checks
vfunctions[vcnt++] = f;
return 0;
}
...
int main(void) {
...
// iterate vfunctions[] and call the functions
...
}
... и в каком-то другом модуле:
typedef int (*function_t)(void);
extern int add2init(function_t f);
#define M_add2init(function_name) static int int_ ## function_name = add2init(function_name)
int foo(void)
{
printf("foo\n");
return 0;
}
M_add2init(foo); // <--- register foo function
Вы можете использовать расширение gcc __ attribute __ ((constructor))
, если gcc подходит для вашего проекта.
#include <stdio.h>
void func1() __attribute__((constructor));
void func2() __attribute__((constructor));
void func1()
{
printf("%s\n",__func__);
}
void func2()
{
printf("%s\n",__func__);
}
int main()
{
printf("main\n");
return 0;
}
//the output
func2
func1
main
Если ваш отдельный модуль представляет сущность «класса» и имеет конструктор экземпляра, вы можете использовать следующую конструкцию:
static inline void init(void) { ... }
static int initialized = 0;
#define INIT if (__predict_false(!initialized)) { init(); initialized = 1; }
struct Foo *
foo_create(void)
{
INIT;
...
}
где « __ pred_false
» - подсказка для предсказания ветвления вашего компилятора. Когда создается первый объект, модуль инициализируется автоматически (однократно).
Splint (и, возможно, другие варианты Lint) могут выдавать предупреждение о функциях, которые определены, но не вызваны.
Интересно, что большинство компиляторов предупреждают вас о неиспользуемых переменных, но не о неиспользуемых функциях. .
вы можете сделать что-то в этом роде с помощью раздела компоновщика. всякий раз, когда вы определяете функцию инициализации, помещайте указатель на нее в разделе компоновщика только для указателей функций инициализации. тогда вы сможете хотя бы узнать, сколько функций init было скомпилировано.
и если не имеет значения, в каком порядке вызываются функции инициализации, и все они имеют один и тот же прототип, вы можете просто вызвать их все в цикле из main.
точные детали ускользают из моей памяти, но это работает что-то вроде этого :: в файле модуля ...
//this is the syntax in GCC..(or would be if the underscores came through in this text editor)
initFuncPtr thisInit __attribute((section(.myinits)))__= &moduleInit;
void moduleInit(void)
{
// so init here
}
это помещает указатель на функцию инициализации модуля в разделе .myinits, но оставляет код в разделе .code. поэтому раздел .myinits - это не что иное, как указатели. вы можете думать об этом как о массиве переменной длины, к которому могут добавлять файлы модулей.
тогда вы можете получить доступ к начальному и конечному адресам раздела из основного. и идти оттуда.
если все функции инициализации имеют один и тот же прототип, вы можете просто перебирать этот раздел, вызывая их все.
это, по сути, создание вашей собственной статической системы конструкторов на C.
, если вы делаете большой проект, и ваш компоновщик, по крайней мере, не является полнофункциональным, у вас может быть проблема ...
Могу я дать ответ на свой вопрос?
Моя идея заключалась в том, чтобы каждая функция добавляла свое имя в глобальный список функций, как решение Ника Д.
Затем я просматривал таблицу символов, созданную -gstab, и искал любые функции с именем init_ *, которые не были вызваны.
Это встроенное приложение, поэтому у меня есть изображение эльфа во флэш-памяти.
Однако мне не нравится эта идея, потому что это означает, что мне всегда нужно включать отладочную информацию в двоичный файл.
Почему бы не написать сценарий постобработки, который бы проверил за вас. Затем запустите этот сценарий как часть процесса сборки ... Или, еще лучше, сделайте его одним из ваших тестов. Вы ведь пишете тесты? :)
Например, если у каждого из ваших модулей есть файл заголовка, modX.c. И если подпись вашей функции init () - "void init ()" ...
Попросите ваш скрипт grep просмотреть все ваши .h-файлы и создать список имен модулей, которые нужно редактировать с помощью init (). Затем пусть скрипт проверит, действительно ли init () вызывается для каждого модуля в main ().
Большее время работы не проблема
Вы можете реализовать своего рода «конечный автомат» для каждого модуля, в котором действия функции зависят от состояния, в котором находится модуль. . Это состояние может быть установлено на BEFORE_INIT или INITIALIZED.
Например, допустим, у нас есть модуль A с функциями foo и bar.
Фактическая логика функций (то есть, что они на самом деле do) будет объявлен так:
void foo_logic();
void bar_logic();
Или какова бы ни была его подпись.
Затем фактические функции модуля (т. е. фактическая функция, объявленная foo ()), будут выполнять проверку во время выполнения условия модуля, и решите, что делать:
void foo() {
if (module_state == BEFORE_INIT) {
handle_not_initialized_error();
}
foo_logic();
}
Эта логика повторяется для всех функций.
Несколько замечаний: