Pull to refresh
151
0
Max Filippov @jcmvbkbc

low level freak

Send message
Выложу как слетаем.
упомянутый. собственной персоной.
Не упоминайте, чтоли, всуе.
Поясню свою мысль:

В производных классах техник FireSkill, WoodSkill и т.д. конструкторы по умолчанию закрыты, но базовый класс Skill объявлен как friend, что позволяет создавать объекты этих классов только внутри класса Skill.

Зачем такое ограничение? В данном примере объекты-то получатся взаимозаменяемыми, вне зависимости от того, созданы ли они через Skill(int) или непосредственно.

Если это ограничение убрать, и конструкторы сделать открытыми, friend не нужен.
Это плата за то что все конструкторы наследников — приватные. А зачем?
Ну в таком случае вопрос вообще смысла не имеет.
Но если не обращать внимания на эту мелочь (:
Например, заменив delete pA на pA->A::~A(); а delete pB; на pb->B::~B();
Уточню: мы говорим о разрушении оператором delete объекта класса B через указатель на его базовый подобъект класса A, в случае если деструкторы A и B — невиртуальные.

Деструктор класса A будет вызван в любом случае. Вне зависимости от его виртуальности. И деструкторы всех предков A, если бы таковые были. Компилятор просто знает что их нужно вызвать, видя что delete применяется к A*. (Вас же не удивляет, что будь тут написано не

A * pA = new B; delete pA;

а

B * pB = new B; delete pB;

деструкторы будут вызваны сначала для B а потом и для A?)

В чем теперь ваш вопрос?
Если следовать RAII, то каждый класс в иерархии должен в своем деструкторе освободить занятые именно этим классом ресурсы. Можно сделать это в отдельных функциях, вызываемых деструктором. Очевидно что таким функциям не нужно быть виртуальными. Виртуальным должен быть только деструктор того класса, к которому применяется delete.
а если

class Derived: public Base{
  void stop_thread() {...}
public:
  Derived() {pthread_create(...);}
  ~Derived() {stop_thread();pthread_join(...);}
};


И кроме того, такой delete — это неопределенное поведение, как сказано ниже.
~~ Попытка самоубийства ненамного лучше гемофилии (а может и хуже).
Базовый класс сам ее освободит.
Как бы там ни было, стандарт, в пункте 5.3.5:3 говорит о том, что если статический тип аргумента delete отличается от динамического типа объекта, а деструктор аргумента не является виртуальным, то поведение не определено.
В моих примерах не одни только методы. Я к тому, что данные бывают разными.
А кроме того, будь это правило действительно хорошим, оно было бы частью стандарта, вы не находите?
Происходит это потому, что удаление производится через указатель на базовый класс и для вызова деструктора компилятор использует раннее связывание. Деструктор базового класса не может вызвать деструктор производного, потому что он о нем ничего не знает. В итоге часть памяти, выделенная под производный класс, безвозвратно теряется.


Ну в вашем-то примере никакой утечки не будет. Механизм описан верно, но конечный вывод не совсем корректен: пока классы-наследники не выделяют ресурсов, на освобождение которых они могли бы рассчитывать при вызове свооего деструктора, всё будет в порядке. Такими ресурсами могут быть как динамическая память, так и внешние объекты: файлы, таймеры,… А память под объект класса будет выделена одним куском, одним куском она и освободится, вне зависимости от того, через какой указатель.

Например:

class B: public A {
  char m_str[1000];
};

— не будет утечек.

class B: public A {
  std::string m_str;
};

— будут утечки.
Удобство cinoptions в том, что там есть по управляющей конструкции практически на любой элемент форматирования, упоминаемый в большинстве стандартов кодирования. И подправить это выражение достаточно просто. А кроме того, indentexpr для С и С++ выполняется с помощью cindent, согласно $VIMRUNTIME/indent/{c,cpp}.vim
Еще более life-changing будет cindent, автоматически применяющий выбранный с помощью cinoptions стиль кодирования, как при вводе, так и при переформатировании командой =
Через нечеткость формулировки: как бы числа можно на звуковуху подать и будет звук. А от символов — не будет. Декодирование в некотором роде.
… а оригинал переписать по Горнеру:
xor eax,eax
xor ebx,ebx
mov edx,10
decode:
mov bl, byte ptr [si]
inc si
sub bl, '0'
jc done
cmp bl, 10
jnc done
imul eax,edx
add eax,ebx
jmp decode
done:
mov dword ptr cs:[len], eax
ret
proc ctoh

mov dword ptr cs:[string],'0000' ; а это и есть собственно декодер аудио
mov dword ptr cs:[string+4],'0o000'
mov cx,0
ctoh1: cmp byte ptr [si],30h \ цикл поиска последовательности десятичных цифр
jc ctoh2                     | занятно, что нет ограничения на максимальную длину последовательности
cmp byte ptr [si],3ah        |
jnc ctoh2                    |
inc si                       | si -- текущая рассматриваемая цифра
inc cx                       | cx -- текущая длина строки
jmp ctoh1                    /
ctoh2:
mov di,offset cs:string+7    \ копирование найденной строки цифр в буфер string
push si                      | начиная с конца
dec si                       |
std                          | задом наперед
rep movsb                    | buffer overflow, если символов было больше 8
pop si                       |
inc si                       /
mov bx,offset cs:string+7    \ подготовка к получению числового значения десятичного числа записанного строкой
mov edx,1                    | текущий вес младшего разряда
mov cx,8                     | количество разрядов
mov dword ptr cs:[len],0     / обнулили значение результата
ctoh_loop1:                  \ цикл умножения цифр числа на веса их разрядов
mov eax,0                    |
mov al,byte ptr cs:[bx]      | достать цифру
sub al,30h                   | перевести символ '0'-'9' в число 0-9
imul eax,edx                 | умножить на вес текущего разряда
add dword ptr cs:[len],eax   | добавить к результату
imul edx,10                  | увеличить вес разряда
dec bx                       | перейти к предыдущей цифре
loop ctoh_loop1              /
ret

ctoh endp


Резюме: можно и это выкинуть.
По закону Мёрфи — будет. См. ниже

Information

Rating
Does not participate
Location
Fremont, California, США
Registered
Activity