C ++: передача указателя на шаблонный класс в C - PullRequest
2 голосов
/ 26 марта 2012

Я пишу API оболочки C для моей библиотеки.

Обычно я передаю свои объекты C ++ как void* в C. И, естественно, для каждой публичной функции каждого объекта есть функция доступа к оболочке. Код C не не имеет доступа к собственным членам класса C ++.

Вчера кто-то упомянул в IRC, что я не должен передавать указатель на классы шаблонов C ++ как void* в C, потому что это опасно. Это правда? Насколько отличаются указатели на обычные классы C ++ от указателей до шаблонных классов?

Спасибо!

Ответы [ 5 ]

7 голосов
/ 26 марта 2012

Это подделка.Шаблоны не имеют специальных свойств, которые могут быть получены обычным классом.Убедитесь, что вы всегда используете подходящий актерский состав, и все будет в порядке.

2 голосов
/ 26 марта 2012

Это не относится к шаблонам по сравнению с обычными классами, но если ваш класс имеет множественное наследование, вы всегда должны начинать с одного и того же типа перед приведением к void*, а также при приведении обратно.Адрес указателя будет меняться в зависимости от того, к какому родительскому классу относится тип указателя.

class ParentA
{
    // ...
    int datumA;
};

class ParentB
{
    // ...
    int datumB;
};

class Derived : public ParentA, public ParentB
{
    // ...
};

int main()
{
    Derived d;
    ParentA * ptrA = &d;
    ParentB * ptrB = &d;
    assert((void*)ptrA == (void*)ptrB); // asserts!
}
1 голос
/ 27 марта 2012

Всегда безопасно навести указатель Foo* на void*, а затем восстановить на тот же тип Foo*.Однако при использовании наследования следует проявлять особую осторожность.Upcasting / downcasting не должно быть сделано через указатель void*.Рассмотрим следующий код:

#include <cassert>

class Parent
{
    int bar;
};

class Derived : public Parent
{
    virtual void foo() { }
};

int main()
{
    Derived d;
    Derived* ptr_derived = &d;
    void *ptr_derived_void = ptr_derived;
    Derived*    ptr_derived_from_void = (Derived*)ptr_derived_void;

    assert(ptr_derived_from_void == ptr_derived);   //that's OK

    Parent* ptr_parent = ptr_derived;   //upcast
    Parent* ptr_parent_from_void = (Parent*)ptr_derived_void;   //upcast?

    assert(ptr_parent_from_void == ptr_parent); //that's not OK

    return 0;
}

Также этот пост показывает некоторые проблемы с приведением через void*.

0 голосов
/ 26 марта 2012

Указатели - это просто указатели на данные.«Тип» указателя на самом деле не имеет никакого значения (во время компиляции), он только для удобства чтения и поддержки кода.

Например:

 Foo<a> *x = new Foo<a>();
 void *y = (void*)x;
 Foo<a> *z = (Foo<a>*)y;

Совершенно верно и не вызовет никаких проблем.Единственная проблема при приведении типов указателей состоит в том, что вы можете столкнуться с проблемами разыменования, когда вы забыли, что на самом деле является базовым типом данных, на который ссылается указатель.

Если вы везде обойдете void *, простобудьте осторожны, чтобы сохранить целостность типов.

Не делайте что-то вроде:

Foo<a> *x = new Foo<a>();
void *z = (void*)x;
//pass around z for a while, then mistakenly...
Foo<b> *y = (Foo<b>*)z;

Случайно!

0 голосов
/ 26 марта 2012

Класс шаблона, созданный с классами A и B в качестве аргументов шаблона, включает в себя компилятор, создающий два отдельных класса, которые специально обрабатывают эти соответствующие типы.Во многих отношениях это похоже на интеллектуальный строго типизированный макрос препроцессора.Ничего не отличается от ручного копирования и вставки двух отдельных «обычных» классов, которые работают на A и B соответственно.Так что нет.

Единственный способ, которым это будет опасно, - это если вы попытаетесь разыграть то, что было изначально:

MyClass<A>

как

MyClass<B>

, если A не равно B, поскольку они могут иметь разную структуру памяти.

...