Подсчет ссылок в C++

Я реализую математическую библиотеку на C++. Библиотека будет скомпилирована в DLL, поэтому тем, кто ее использует, потребуются только файлы заголовков с определениями классов.

Мои классы будут посещать люди, которые плохо знакомы с языком. Однако есть некоторые объекты, на которые можно ссылаться в нескольких частях их программ. Поскольку я не ожидаю, что они будут заниматься управлением памятью, я хотел бы сделать это сам. Поэтому мне нужно реализовать подсчет ссылок (сборка мусора невозможна).

Я хочу сделать подсчет ссылок максимально прозрачным, например ...

// Define a Bézier curve
CVecList pts;
pts.Add(Vector(0,0,0));
pts.Add(Vector(0,0,100));
pts.Add(Vector(0,100,0));
pts.Add(Vector(0,100,100));
CCurve* c1 = new CBezier(pts);

// Define a 3rd order B-Spline curve
pts.Clear();
pts.Add(Vector(0,0,0));
pts.Add(Vector(0,200,100));
pts.Add(Vector(0,200,200));
pts.Add(Vector(0,-200,100));
pts.Add(Vector(0,-200,200));
pts.Add(Vector(0,0,0));
CCurve* c2 = new CBSpline(pts,3);

// The Bézier curve object must be deleted automatically
// because the only reference to it has been released
// Similar to IUnknown::Release() in COM
c1 = c2;

Когда я определяю объекты поверхности, все становится немного сложнее, потому что некоторые поверхности определяются в терминах двух кривых:

CVecList pts;
// ...
CCurve* f = new CBezier(pts);

pts.Clear();
// ...
CCurve* g = new CBezier(pts);

// Mixed surface: S(u,v) = (1-v)*f(u) + v*g(u)
CSurface* s = new CMixed(f,g);

// There are two references to the first Bézier curve,
// the first one is f
// the second one is hidden in a member of CMixed

// Something similar applies to the second Bézier curve

Я подумал, что переопределение operator = для указателей могло помочь:

// This is what I tried, but it's illegal:
typedef CReferenceCounted* PRC;
PRC& operator =(PRC& dest, PRC& source)
{
    if (source)
        source->AddRef();
    if (dest)
        dest->Release();
    memcpy(&dest,&source,sizeof(PRC));
    return dest;
}

... но потом я обнаружил, что operator = недействителен, если он не является нестатическим членом класса.

Может ли кто-нибудь мне помочь?

ootips.org/yonat/4dev/smart-pointers.html
derobert 16.12.2008 08:10

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

Eduardo León 16.12.2008 08:22

Эдуардо, не могли бы вы отредактировать этот пост, выбрать код в своем вопросе, затем нажать кнопку с нулями и единицами? Это применит подсветку синтаксиса к коду.

TM. 16.12.2008 09:04

Я не знал этого трюка. (Я здесь новичок) Спасибо, TM.

Eduardo León 16.12.2008 09:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
4
10 942
5

Ответы 5

Вы пытались перегрузить оператор для скалярных типов. C++ не позволяет вам делать это, кроме перечислений (кроме того, что оператор = должен быть членом). По крайней мере, один из типов должен быть типом, определяемым пользователем. Таким образом, вы хотите обернуть необработанный указатель в определенный пользователем класс, который перегружает конструктор, конструктор копирования, оператор присваивания копии и деструктор и выполняет правильный подсчет ссылок. Это идеальная ситуация для boost::shared_ptr, который делает именно это:

boost::shared_ptr<CCurve> c1(new CBezier(pts));

То же самое и с поверхностями:

CVecList pts;
// ...
boost::shared_ptr<CCurve> f(new CBezier(pts));

pts.Clear();
// ...
boost::shared_ptr<CCurve> g(new CBezier(pts));

// Mixed surface: S(u,v) = (1-v)f(u) + vg(u)
boost::shared_ptr<CSurface> s(new CMixed(f,g)); 

Носите этот умный указатель, и он автоматически управляет временем жизни указанного объекта: если последняя копия указателя выходит за пределы области видимости, объект, на который он указывает, освобождается. shared_ptr разработан, чтобы быть простым в использовании. По возможности старайтесь избегать работы с необработанными указателями. Взгляните на эти умные указатели, они облегчат вашим программистам жизнь с C++ :)

Редактировать: если вы собираетесь обернуть shared_ptr, вы можете сделать это, используя идиому pimpl (handle / body):

/* ---- wrapper in header file bezier.hpp */

struct CBezier {
    CBezier(CVecList const& list);
    void do_calc();
    // ...

private:
    struct CBezierImpl;
    boost::shared_ptr<CBezierImpl> p;
};

/* ---- implementation file bezier.cpp */

// private implementation
struct CBezier::CBezierImpl {
    CBezierImpl(CVecList const& list);
    void do_calc();
    // ...
};


CBezier::CBezier(CVecList const& list)
:p(new CBezierImpl(list)) {

}

void CBezier::do_calc() {
    // delegate to pimpl
    p->do_calc();
}

// ...

Я бы сделал что-то вроде определения CCurve как CCurved_internal, затем typedef CCurve для boost :: shared_ptr <CCurve_internal> ,,,

BubbaT 16.12.2008 08:22

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

Eduardo León 16.12.2008 08:23

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

Johannes Schaub - litb 16.12.2008 08:28

Пожалуйста, избегайте переопределения подсчета ссылок. Работать в многопоточной среде гораздо сложнее, чем вы думаете. К тому же реализация shared_ptr в Boost великолепна.

fulmicoton 16.12.2008 08:35

да, Пол, это идея обертывания shared_ptr. Я также не вижу причин изобретать велосипед

Johannes Schaub - litb 16.12.2008 08:43

Я не буду изобретать велосипед. Я просто заверну shared_ptr ... Однако у меня нет предыдущего опыта работы с Boost, поэтому я, возможно, скоро вернусь с другими вопросами ... :)

Eduardo León 16.12.2008 08:46

Я полностью согласен с этим. Использование shared_ptr - лучший способ.

Nick Haddad 16.12.2008 19:56

Я бы рекомендовал intrusive_ptr вместо shared_ptr для объектов, которыми вы можете управлять для повышения производительности и удобства использования, поскольку вы можете назначить необработанный указатель на intrusive_ptr позже, поскольку счетчик ссылок встроен в объект.

Если вы разрабатываете математическую библиотеку, потратьте много времени на размышления о том, могут ли ваши классы выглядеть как int или std :: complex. Иными словами, иметь ценности ведут себя как ценности. Например.

std::vector<math::point3d> pts;
pts.push_back(math::point3d(0,0,0));
pts.push_back(math::point3d(110,0,0));
pts.push_back(math::point3d(0,100,0));
pts.push_back(math::point3d(0,0,100));
CCurve c1 = make_bezier(pts);

The users of my classes will be people who are new to the language.

Ваш класс предназначен для курса программирования?

Если это так, я бы избегал использования указателей и использовал только конструкторы копирования / присваивание:

  • Производительность / память не является приоритетом
  • Самостоятельное управление памятью покажет довольно плохой пример того, как использовать new / delete.
  • Использование любого типа интеллектуального указателя без знания управления памятью в дальнейшем может вызвать массу недоразумений.

Я согласен с Guishu и MSalters. Даже если это не для курса программирования, было бы неплохо более внимательно имитировать математический вид (например, vector3 = vector1 + vector2 и т. д.).

Что вы также можете сделать, так это использовать копирование при записи (перемонтирование является логическим следствием), но только внутренне. Это может дать вам достаточно быстрые назначения, исключить управление кучей на стороне клиента и сходство с математической нотацией.

Обратите внимание, однако, на то, что для C++ доступны математические библиотеки (TNT, вне моей головы). Вы думали о том, чтобы основывать свою работу на этом?

Другие вопросы по теме