Заблокирован
1

Можно ли удалить объект экземпляра класса из самого себя?

18.11.2014, 15:10. Показов 13979. Ответов 67
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Привет!
Допустим:
C++
1
2
3
4
5
6
7
8
9
10
class TEST
{
public:
    TEST();
    ~TEST();
    void DelMe()
    {
        //DEL ME :-)
    }
};
Вот в теле функции DelMe можно как то удалить собственный экземпляр класса? То есть удалить самого себя из себя?

Добавлено через 4 минуты
Вот так вроде работает, но как - то это ..... Так память нормально освобождается?
C++
1
2
3
4
  void DelMe()
    {
        delete this;
    }
деструктор вроде вызывается

Добавлено через 4 минуты
Нашёл себе методичку: http://www.parashift.com/c++-f... -this.html
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
18.11.2014, 15:10
Ответы с готовыми решениями:

Можно ли создать объект класса с таким же именем как у самого класса?
Можно ли создать объект класса с таким же именем как у самого класса? Ну или как то обойти эту...

Удаление экземпляра класса в функции самого класса (Ошибка при отладке)
Допустим, у нас есть класс Buffer, который хранит в себе указатель на класс некоторой матрицы и...

как сделать чтобы объект производного класса сам себя добавлял в список или массив указателей базового класса?
я хотел так, но программа просто падает void Student::add(Base** head) { cout << "I here" <<...

Можно ли заставить класс записывать самого себя в файл?
то есть class A{ int q,w,e; bool Write(); }; main(){ A a1; a1.write();// и он записывает...

67
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
19.11.2014, 22:43 41
Author24 — интернет-сервис помощи студентам
Цитата Сообщение от hoggy Посмотреть сообщение
А в том, что в любом контексте, где доступен this (скоуп, в котором виден this объекта) this должен быть валиден.
И как это мешает создать метод, в котором будет такой код:
C++
1
2
3
4
void MyClass::Release()
{
    delete this;
}
А использоваться он будет так:
C++
1
2
3
4
5
MyClass * p = new MyClass();
 
//.....
 
p->Release();
?
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
19.11.2014, 22:48 42
Цитата Сообщение от DrOffset Посмотреть сообщение
И как это мешает создать метод
Никак.

с++ не запрещает вам делать UB.
0
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
19.11.2014, 22:51 43
Цитата Сообщение от hoggy Посмотреть сообщение
с++ не запрещает вам делать UB.
И почему же это UB?
1
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
19.11.2014, 22:53 44
Цитата Сообщение от DrOffset Посмотреть сообщение
И почему же это UB?
Цитата Сообщение от hoggy Посмотреть сообщение
this должен быть валиден.
-----------------------------------------------------
0
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
19.11.2014, 22:56 45
Цитата Сообщение от hoggy Посмотреть сообщение
this должен быть валиден.
А он валиден.
Предлагаю почитать вот это.
0
Форумчанин
Эксперт CЭксперт С++
8215 / 5045 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
20.11.2014, 01:12 46
Цитата Сообщение от hoggy Посмотреть сообщение
с++ не запрещает вам делать UB.
До тех пор, пока не произошло обращение по пустому указателю - он сам по себе не вызывает UB.
Да, delete this не является хорошей практикой, но с точки зрения стандарта всё ок.
0
Ушел с форума
Эксперт С++
16473 / 7436 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
20.11.2014, 09:21 47
Цитата Сообщение от hoggy Посмотреть сообщение
Никак.
с++ не запрещает вам делать UB.
Пожалуйста, ссылку на пункт Стандарта (98-03-11 или выше), из которого
явно следует, что "delete this" (не из деструктора !) - UB.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
23.11.2014, 01:57 48
Цитата Сообщение от DrOffset Посмотреть сообщение
Предлагаю почитать вот это.
Не интересно.

Цитата Сообщение от MrGluck Посмотреть сообщение
До тех пор, пока не произошло обращение по пустому указателю - он сам по себе не вызывает UB.
Никаких "если".

Кстати, вызов метода по невалидному указателю так же является UB

C++
1
2
some* p = nullptr;
p->work();
И не важно, трогает ли метод work данные-члены, или нет. Это все равно UB.

Однако UB не означает, что непременно программа должна рухнуть.
UB означает "поведение не определено". Другими словами никто вам больше ничего не гарантирует.
Вся ответственность полностью на плечах программиста.

Как программист, который знает что делает, вы можете рассуждать о любых возможных "если".
Это уже ваше личное дело. И даже если вы решили, что в данной ситуации беды не должно быть, от этого UB не перестает быть UB.

C++
1
2
3
4
5
6
7
// разыменовали указатель
struct sock *sk = tun->sk;  
…
 
if (!tun) //<--- компилятор выпилил проверку, потому что разменовывать невалидный указатель это UB
               // а значит tun всегда валиден, потому что чуть выше его разыменовывали
    return POLLERR;  // if tun is NULL return error
(ц)Знаменитый баг обнаруженный в коде Linux Kernel.

Цитата Сообщение от Убежденный Посмотреть сообщение
Пожалуйста, ссылку на пункт Стандарта (98-03-11 или выше),
Честно? Лень искать.

Если вам это интересно, то вы можете попробовать загуглить на хабре. Там вроде бы были какие то ссылки на стандарт.

Ну или вы можете проверить следующую информацию:

AC>Ну, помниться был такой классный топик, что-то типа NULL и this. И насколько я понимаю _НИГДЕ_ не написано что this _НЕ_МОЖЕТ_ быть нулевым. Разве что ((X*)0)->f имеет неопределённое поведение в общем случае и совершенно определённое для вызова невиртуальной функции по типу указателя для которого эта функция была определена. Тоесть явного запрета на 0 as this нигде нет. Так что насщёт хака можно и поспорить )))
Потому что этот запрет следует из двух положений:
1) 9.3.2/1 In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called.
2) не существует объектов с адресом 0.
(ц)На киберфоруме запрещено делать ссылки на сторонние сайты.
0
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
23.11.2014, 03:04 49
Цитата Сообщение от hoggy Посмотреть сообщение
Не интересно.
Т.е. хабрахабр - это авторитетный источник, а stackoverflow.com - нет?

Цитата Сообщение от hoggy Посмотреть сообщение
Если вам это интересно, то вы можете попробовать загуглить на хабре.
Если речь про эту статью, то она как бы немного о другом.

Цитата Сообщение от hoggy Посмотреть сообщение
Потому что этот запрет следует из двух положений:
1) 9.3.2/1 In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called.
2) не существует объектов с адресом 0.
Какая связь между использованием delete this и сравнением this с нулем?

В общем, как понимать эту фразу?
Цитата Сообщение от hoggy Посмотреть сообщение
this должен быть валиден.
И при чем тут рассуждения на тему сравнения this с нулем? Насчет этой темы вопросов вообще нет, я полностью согласен. Но нельзя доказывать одно, приводя примеры из другой оперы.

Да, в определенных ситуациях, delete this может привести к последующему UB:
1) Вызов из деструктора.
2) Вызов метода или обращение к данным следом за delete this.
В иных случаях оно к UB, увы, не приводит.
0
Неэпический
17869 / 10634 / 2054
Регистрация: 27.09.2012
Сообщений: 26,736
Записей в блоге: 1
23.11.2014, 05:53 50
delete this вполне легальная штука.
Но нужно точно знать, что:
- объект выделен с помощью new
- далее данный объект не будет нигде и никем использоваться, в том числе и в данной функции, после delete this


delete this не приводит к UB, а вот использование this после сие действа обернется катастрофой.
0
4226 / 1795 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
23.11.2014, 06:00 51
По идее delete this и должен быть валиден, так как this - это указатель. ИМХО чтоб эту фичу юзать, странное нужно приложение ещё с той архитектурой, так как в обычных она не нужна.
0
Неэпический
17869 / 10634 / 2054
Регистрация: 27.09.2012
Сообщений: 26,736
Записей в блоге: 1
23.11.2014, 06:05 52
Цитата Сообщение от taras atavin Посмотреть сообщение
ИМХО чтоб эту фичу юзать, странное нужно приложение ещё с той архитектурой, так как в обычных она не нужна.
Например, если нужно корректно удалить объект, использующий полиморфизм, но не имеющий виртуального деструктора:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
 
class A
{
public:
    ~A() { std::cout << "~A()" << std::endl ; }
    virtual void Release () = 0 ;
} ;
 
 
class B : public A
{
    ~B() { std::cout << "~B()" << std::endl ; }
    void Release () { delete this ; }
} ;
 
 
int main()
{
    A * p = new B ;
    p->Release() ;
}
Этим пользуются, например, при разработке библиотек, для которых нужна совместимость ABI, а наличие виртуального деструктора эту самую совместимость ломает
0
Ушел с форума
Эксперт С++
16473 / 7436 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
23.11.2014, 10:29 53
Цитата Сообщение от hoggy Посмотреть сообщение
Честно? Лень искать.
В таком случае я отписываюсь.

Цитата Сообщение от hoggy Посмотреть сообщение
Если вам это интересно, то вы можете попробовать загуглить на хабре. Там вроде бы были какие то ссылки на стандарт.
C++03, "5.3.5 Delete" и "9.3.2 The this pointer": ни слова о том,
что "delete this" ведет к неопределенному поведению.
Ни явно, ни косвенно.

И зачем Хабр ?
Давайте заглянем на более авторитетный в таких вопросах ресурс - isocpp.org:

Memory Management
https://isocpp.org/wiki/faq/fr... elete-this
Q: Is it legal (and moral) for a member function to say delete this?

A: As long as you’re careful, it’s okay (not evil) for an object to commit suicide (delete this).

Here’s how I define “careful”:

1. You must be absolutely 100% positively sure that this object was allocated
via new (not by new[], nor by placement new, nor a local object on the stack, nor a
namespace-scope / global, nor a member of another object; but by plain ordinary new).

2. You must be absolutely 100% positively sure that your member function will be the
last member function invoked on this object.

3. You must be absolutely 100% positively sure that the rest of your member
function (after the delete this line) doesn’t touch any piece of this object
(including calling any other member functions or touching any data members).
This includes code that will run in destructors for any objects allocated on the
stack that are still alive.

4. You must be absolutely 100% positively sure that no one even touches the this
pointer itself after the delete this line. In other words, you must not examine it,
compare it with another pointer, compare it with nullptr, print it, cast it, do
anything with it.

Naturally the usual caveats apply in cases where your this pointer is a pointer to a
base class when you don’t have a virtual destructor.
---

Цитата Сообщение от hoggy Посмотреть сообщение
Ну или вы можете проверить следующую информацию:
AC>Ну, помниться был такой классный топик, что-то типа NULL и this. И насколько я понимаю _НИГДЕ_ не написано что this _НЕ_МОЖЕТ_ быть нулевым. Разве что ((X*)0)->f имеет неопределённое поведение в общем случае и совершенно определённое для вызова невиртуальной функции по типу указателя для которого эта функция была определена. Тоесть явного запрета на 0 as this нигде нет. Так что насщёт хака можно и поспорить )))

Потому что этот запрет следует из двух положений:
1) 9.3.2/1 In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called.
2) не существует объектов с адресом 0.
Все верно. This - это адрес объекта, а объектов с адресом 0 в С++ существовать не может.
Только к "delete this" это не имеет никакого отношения.
2
4226 / 1795 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
23.11.2014, 10:37 54
Цитата Сообщение от Croessmah Посмотреть сообщение
Например, если нужно корректно удалить объект, использующий полиморфизм, но не имеющий виртуального деструктора:
А почему у него нет деструктора?
0
120 / 142 / 46
Регистрация: 31.10.2014
Сообщений: 721
Записей в блоге: 1
23.11.2014, 11:09 55
Можно
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
23.11.2014, 12:02 56
Цитата Сообщение от DrOffset Посмотреть сообщение
Т.е. хабрахабр - это авторитетный источник, а stackoverflow.com - нет?
Не интересно, потому что ничего нового.

Цитата Сообщение от DrOffset Посмотреть сообщение
Если речь про эту статью, то она как бы немного о другом.
Все что вам нужно - это понять логику компилятора: на каком основании он имеет право оптимизировать проверку if ( !this

Оптимизирующий компилятор закладывается на то, что this всегда валидный (иначе - это UB).
А значит делать проверку не обязательно.
И выпиливает её.

Цитата Сообщение от DrOffset Посмотреть сообщение
В общем, как понимать эту фразу?
На период выполнения нестатической функции-члена, указатель this должен указывать на объект.
Или поведение не определено.

Цитата Сообщение от Убежденный Посмотреть сообщение
Все верно. This - это адрес объекта, а объектов с адресом 0 в С++ существовать не может.
Только к "delete this" это не имеет никакого отношения.
Я уже писал выше, что технически конструкция delete this сработает.
Речь о последствиях.
После уничтожения объекта, this становится невалиден.
0
4226 / 1795 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
23.11.2014, 12:13 57
Кстати, указателю this нельзя после delete this присвоить NULL.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
 public:
  float x;
  float y;
 void f()
 {
  delete this;
 }
};
...
A *p=NULL;
...
p=new A;
...
p->f();
...
if (p!=NULL)
{
 std::cout<<p->x; // Здесь будет егог доступа к невалидному адресу.
}
.
0
3257 / 2059 / 351
Регистрация: 24.11.2012
Сообщений: 4,909
23.11.2014, 12:14 58
Цитата Сообщение от taras atavin Посмотреть сообщение
Кстати, указателю this нельзя после delete this присвоить NULL
Естественно, он же константный.
0
Неэпический
17869 / 10634 / 2054
Регистрация: 27.09.2012
Сообщений: 26,736
Записей в блоге: 1
23.11.2014, 13:18 59
Цитата Сообщение от taras atavin Посмотреть сообщение
А почему у него нет деструктора?
Спёрто из книги Дональд Бокс - Сущность технологии COM (крышка 1)

Оказывается, применение техники разделения интерфейса и реализации может решить и проблемы совместимости транслятора/компоновщика C++. При этом, однако, определение класса интерфейса должно принять несколько иную форму. Как отмечалось ранее, проблемы совместимости возникают из-за того, что разные трансляторы имеют различные соображения по поводу того, как

1. передавать особенности языка на этапе выполнения;
2. символические имена будут представлены на этапе компоновки.

Если бы кто-нибудь придумал, как скрыть детали реализации транслятора/компоновщика за
каким-либо двоичным интерфейсом, это сделало бы написанные на C++ библиотеки DLL значительно
более широко используемыми.

Двоичная защита, то есть тот факт, что класс интерфейса C++ не использует языковых конструкций, зависящих от транслятора, решает проблему зависимости от транслятора/компоновщика. Чтобы сделать эту независимость более полной, необходимо в первую очередь определить те аспекты языка, которые имеют одинаковую реализацию в разных трансляторах. Конечно, представление на этапе выполнения таких сложных типов, как С-структуры (structs), может быть выдержано инвариантным по отношению к трансляторам. Это — основное, что должен делать системный интерфейс, основанный на С, и иногда это достигается применением условно транслируемых определений типа прагм (pragmas) или других директив транслятора. Второе, что следует сделать, — это заставить все компиляторы проходить параметры функций в одном и том же порядке (слева направо, справа налево) и зачищать стек также одинаково. Подобно совместимости структур, это также решаемая задача, и для унификации работы со стеком часто используются условные директивы транслятора. В качестве примера можно привести макросы из Win32 API. Каждая извлеченная из системных DLL функция определена с помощью этих макросов:

C++
1
WINAPI/WINBASEAPIWINBASEAPI void WINAPI Sleep(DWORD dwMsecs);
Каждый разработчик транслятора определяет эти символы препроцессора для создания гибких стековых фреймов. Хотя в среде производителей может возникнуть желание использовать аналогичную методику для определений всех методов, фрагменты программ в этой главе для большей наглядности ее не используют.

Третье требование к независимости трансляторов — наиболее уязвимое для критики из всех, так как оно делает возможным определение двоичного интерфейса: все трансляторы C++ с заданной платформой одинаково осуществляют механизм вызова виртуальных функций. Действительно, это требование единообразия применимо только к классам, не имеющим элементов данных, а имеющим не более одного базового класса, который также не имеет элементов данных. Вот что означает это требование для следующего простого определения класса:

C++
1
2
3
4
5
class calculator { 
  public: 
    virtual void add1(short x); 
    virtual void add2(short x, short y); 
};
Все трансляторы с данной платформой должны создать эквивалентные последовательности машинного кода для следующего фрагмента программы пользователя:

C++
1
2
3
extern calculator *pcalc; 
pcalc->add1(1); 
pcalc->add2(1, 2);
Отметим, что требуется не идентичность машинного кода на всех трансляторах, а его эквивалентность. Это означает, что каждый транслятор должен делать одинаковые допущения относительно того, как объект такого класса размещен в памяти и как его виртуальные функции динамически вызываются на этапе выполнения.


Впрочем, это не такое уж блестящее решение проблемы, как может показаться. Реализация виртуальных функций на C++ на этапе выполнения выливается в создание конструкций vptr и vtbl практически на всех трансляторах. При этой методике транслятор молча генерирует статический массив указателей функций для каждого класса, содержащего виртуальные функции. Этот массив называется vtbl (virtual function table — таблица виртуальных функций) и содержит один указатель функции для каждой виртуальной функции, определенной в данном классе или в ее базовом классе. Каждый объект класса содержит единственный невидимый элемент данных, именуемый vptr (virtual function pointer - указатель виртуальных функций); он автоматически инициализируется конструктором для указания на таблицу vtbl класса. Когда клиент вызывает виртуальную функцию, транслятор генерирует код, чтобы разыменовать указатель vptr, занести его в vtbl и вызвать функцию через ее указатель, найденный в назначенном месте. Так на C++ обеспечивается полиморфизм и диспетчеризация динамических вызовов. Рисунок 1.5 показывает представление на этапе выполнения массивов vptr/vtbl для класса calculator, рассмотренного выше.


Можно ли удалить объект экземпляра класса из самого себя?



Фактически каждый действующий в настоящее время качественный транслятор C++ использует базовые концепции vprt и vtbl. Существует два основных способа размещения таблицы vtbl: с помощью CFRONT и корректирующего переходника ( adjuster thunk). Каждый из этих приемов имеет свой способ обращения с тонкостями множественного наследования. К счастью, на каждой из имеющихся платформ доминирует один из способов (трансляторы Win32 используют adjuster thunk, Solaris — стиль CFRONT для vtbl). К тому же формат таблицы vtbl не влияет на исходный код C++, который пишет программист, а скорее является артефактом сгенерированного кода. Желающие узнать подробности об этих двух способах могут обратиться к прекрасной книге Стэна Липпмана "Объектная модель C++ изнутри" (Stan Lippman. Inside C++ Object Model).


Основываясь на столь далеко идущих допущениях, теперь можно решить проблему зависимости от транслятора. Предполагая, что все трансляторы на данной платформе одинаково реализуют механизм вызова виртуальной функции, можно определить класс интерфейса C++ так, чтобы глобальные операции над типами данных определялись в нем как виртуальные функции; тогда можно быть уверенным, что все трансляторы будут генерировать эквивалентный машинный код для вызова методов со стороны клиента. Это предположение об единообразии означает, что ни один класс интерфейса не имеет элементов данных и ни один класс интерфейса не может быть прямым потомком более чем одного класса интерфейса. Поскольку в классе интерфейса нет элементов данных, эти методы практически невозможно использовать.

C++
1
2
3
4
5
6
// ifaststring.h 
class IFastString { 
  public: 
    virtual int Length(void) const = 0; 
    virtual int Find(const char *psz) const = 0; 
};

Определение этих методов как чисто виртуальных также дает знать транслятору, что от класса интерфейся не требуется никакой реализации этих методов. Когда транслятор генерирует таблицу vtbl для класса интерфейса, входная точка для каждой простой виртуальной функции является или нулевой ( null), или точкой входа в С-процедуру этапа выполнения (_purecall в Microsoft C++), которая при вызове генерирует логическое утверждение. Если бы метод не был определен как чисто виртуальный, транслятор попытался бы включить в соответствующую входную точку vtbl системную реализацию метода класса интерфейса, которая в действительности не существует. Это вызвало бы ошибку компоновки. Определенный таким образом класс интерфейса является абстрактным базовым классом. Соответствующий класс реализации должен порождаться классом интерфейса и перекрывать все чисто виртуальные фyнкции содержательными реализациями. Эта наследственная связь проявится в объектах, которые в качестве своего представления имеют двоичное надмножество представления класса интерфейса (которое как раз и есть vptr/vtbl). Дело в том, что отношение "является" ("is-a") между порождаемым и базовым классами применяется на двоичном уровне в C++ так же, как и на уровне моделирования в объектно-ориентированной разработке:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class FastString : public IFastString {
    const int m_cch; 
      // count of characters 
      // число символов 
    char *m_psz; 
  public: 
    FastString(const char *psz); 
    ~FastString(void); 
    int Length(void) const; 
      // returns # of characters 
      // возвращает число символов 
    int Find(const char *psz) const; 
      // returns offset 
      // возвращает смещение 
};

Поскольку FastString порождается от IFastString , двоичное представление объектов FastString должно быть надмножеством двоичного представления IFastString . Это означает, что объекты FastString , будут содержать указатель vptr, указывающий на совместимую с таблицей vtbl IFastString. Поскольку классу FastString vptr можно приписывать различные конкретные типы данных, его таблица vtbl будет содержать указатели на существующие реализации методов Length и Find. Их связь показана на рис. 1.6.


Можно ли удалить объект экземпляра класса из самого себя?

0
Неэпический
17869 / 10634 / 2054
Регистрация: 27.09.2012
Сообщений: 26,736
Записей в блоге: 1
23.11.2014, 13:20 60
Спёрто из книги Дональд Бокс - Сущность технологии COM (крышка 2)
Даже несмотря на то, что открытые операторы над типами данных подняты до уровня чисто виртуальных функций в классе интерфейса, клиент не может приписывать значения объектам FastString, не имея определения класса для класса реализации. При демонстрации клиенту определения класса реализации от него будет скрыта двоичная инкапсуляция интерфейса; что не позволит клиенту использовать класс интерфейса. Одним из разумных способов обеспечить клиенту возможность использовать объекты FastString является экспорт из DLL глобальной функции, которая будет вызывать новый оператор от имени клиента. При условии, что эта подпрограмма экспортируется с опцией extern "С" , она будет доступна для любого транслятора C++.


C++
1
2
3
4
5
6
7
8
9
10
11
// ifaststring.h 
class IFastString { 
  public: 
    virtual int Length(void) const = 0; 
    virtual int Find(const char *psz) const = 0; 
};
 
extern "C" IFastString *CreateFastString(const char *psz); 
      // faststring.cpp (part of DLL)
IFastString *CreateFastString (const char *psz) 
  { return new FastString(psz); }
Как было в случае класса-дескриптора, новый оператор вызывается исключительно внутри DLL FastString, а это означает, что размер и расположение объекта будут установлены с использованием того же транслятора, который транслировал все методы реализации.
Последнее препятствие, которое предстоит преодолеть, относится к уничтожению объекта. Следующая клиентская программа пройдет трансляцию, но результаты будут непредсказуемыми:
C++
1
2
3
4
5
6
7
int f(void)
{
    IFastString *pfs = CreateFastString("Deface me"); 
    int n = pfs->Find("ace me"); 
    delete pfs; 
    return n; 
}
Непредсказуемое поведение вызвано тем фактом, что деструктор класса интерфейса не является виртуальным. Это означает, что вызов оператора delete не сможет динамически найти последний порожденный деструктор и рекурсивно уничтожит объект ближайшего внешнего типа по отношению к базовому типу. Поскольку деструктор FastString никогда не вызывается, в данном примере из буфера исчезнет строка "Deface me", которая должна там присутствовать.


Очевидное решение этой проблемы — сделать деструктор виртуальным в классе интерфейса. К сожалению, это нарушит независимость класса интерфейса от транслятора, так как положение виртуального деструктора в таблице vtbl может изменяться от транслятора к транслятору. Одним из конструктивных решений этой проблемы является добавление к интерфейсу явного метода Delete как еще одной чисто виртуальной функции, чтобы заставить производный класс уничтожать самого себя в своей реализации этого метода. В результате этого будет выполнен нужный деструктор. Модифицированная версия заголовочного файла интерфейса выглядит так:
C++
1
2
3
4
5
6
7
8
9
// ifaststring.h
class IFastString { 
  public: 
    virtual void Delete(void) = 0; 
    virtual int Length(void) const = 0; 
    virtual int Find(const char *psz) const = 0;
};
 
extern "C" IFastString *CreateFastString (const char *psz);
она влечет за собой соответствующее определение класса реализации:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// faststring.h
#include "ifaststring.h" 
class FastString : public IFastString { 
    const int m_cch; 
      // count of characters 
      // счетчик символов 
    char *m_psz; 
  public: 
    FastString(const char *psz); 
    ~FastString(void); 
    void Delete(void); 
      // deletes this instance 
      // уничтожает этот экземпляр 
    int Length(void) const; 
      // returns # of characters 
      // возвращает число символов 
    int Find(const char *psz) const; 
      // returns offset 
      // возвращает смещение 
};
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// faststring.cpp
#include <string.h> 
#include "faststring.h" 
 
IFastString* CreateFastString (const char *psz) { 
    return new FastString(psz);
} 
 
FastString::FastString(const char *psz) : m_cch(strlen(psz)), m_psz(new char[m_cch + 1]) {
    strcpy(m_psz, psz); 
} 
 
void FastString::Delete(void) { 
    delete this; 
} 
 
FastString::~FastString(void) { 
    delete[] m_psz; 
} 
 
int FastString::Lengtn(void) const { 
    return m_cch; 
} 
 
int FastString::Find(const char *psz) const { 
    // O(1) lookup code deleted for clarity 
    // код поиска 0(1) уничтожен для ясности 
}

Рисунок 1.7 показывает представление FastString на этапе выполнения.
Чтобы использовать тип данных FastString, клиентам надо просто включить в программу файл определения интерфейса и вызвать CreateFastString:
C++
1
2
3
4
5
6
7
8
9
10
11
12
#include "ifaststring.h"
 
int f (void)
{
    int n = -1 ;
    IFastString * pfs = CreateFastString ("Hi Bob!") ;
    if ( pfs ) {
        n = pfs->Find("ob") ;
        pfs->Delete () ;
    }
    return n ;
}
Можно ли удалить объект экземпляра класса из самого себя?


Отметим, что все, кроме одной, точки входа в DLL FastString являются виртуальными функциями. Виртуальные функции класса интерфейса всегда вызываются косвенно, через указатель функции, хранящийся в таблице vtbl, избавляя клиента от необходимости указывать их символические имена на этапе разработки. Это означает, что методы интерфейса защищены от различий в коррекции символических имен на разных трансляторах. Единственная точка входа, которая явно компонуется по имени, — это CreateFastString — глобальная функция, которая обеспечивает клиенту доступ в мир FastString. Заметим, однако, что эта функция была экспортирована с опцией extern "С", которая подавляет коррекцию символов. Следовательно, все трансляторы C++ ожидают, что импортируемая библиотека и DLL экспортируют один и тот же идентификатор. Полезным результатом этой методики является то, что вы можете спокойно извлечь класс из DLL, использующей одну среду C++, а обратиться к этому классу из любой другой среды C++. Эта возможность необходима при построении основы для независимых от разработчика компонентов повторного пользования.
0
23.11.2014, 13:20
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
23.11.2014, 13:20
Помогаю со студенческими работами здесь

Как можно удалить или очистить тело макроса после его исполнения из самого себя?
Скажите пожалуйста! Как можно удалить или очистить тело макроса после его исполнения из самого себя?

Чем отличается объект класса от экземпляра класса
Читаю книгу &quot;PHP Объекты, шаблоны и методики программирования&quot; автор Мэт Зандстра. Перевод на...

Зацыкливания класса на самого себя
scanner =new Scanner(System.in); System.out.println(&quot;Print&quot;); if(scanner.hasNextInt()){int...

Как экземпляру класса изменить тип самого себя?
Есть класс1, наследованный от класса класс-предок. Он лежит в list&lt;класс-предок&gt;. Нужно, не меняя...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru