Личный опыт разработки ПО

Сборник рецептов

Архив за 03 2010

Use of undefined type

5 комментариев »

Давайте поговорим о неприятных граблях связанных с удалением неполного типа, что может привести к крайне неприятным последствиям, например милому сердцу Segmentation fault. Связано это с двумя вещами:

  1. Полномочиями данными компилятору автоматически генерировать деструктор, если он не определен в классе
  2. Возможностью вызвать оператор delete для объекта тип которого в точке удаления еще не известен

Рассмотрим простой код и посмотрим, что делает компилятор:

test.h

#ifndef TEST_H
#define TEST_H
 
class Test
{
public:
	Test();
	~Test();
};
 
#endif//TEST_H

test.cpp

#include <iostream>
 
#include "test.h"
 
Test::Test()
{
	std::cout << "Test" << std::endl;
}
 
Test::~Test()
{
	std::cout << "~Test" << std::endl;
}

trouble.h

#ifndef TROUBLE_H
#define TROUBLE_H
 
#include <memory>
 
class Test;
 
class Trouble
{
public:
	Trouble();
 
private:
	std::auto_ptr<Test> Test_;
};
 
#endif//TROUBLE_H

trouble.cpp

#include "test.h"
 
#include "trouble.h"
 
Trouble::Trouble()
	: Test_(new Test())
{
}

main.cpp

#include "trouble.h"
 
int main(int argc, char* argv[]) 
{
	Trouble trouble;
	return 0;
}

Итак, имеется два класса: Test и Trouble, причем в целях ускорения компиляции разработчик решил сделать предварительное объявление класса Test в trouble.h, но не написал деструктор для класса Trouble, а значит компилятор заботливо создаст деструктор сам. Когда он это сделает? Вообще компилятор не мечется по коду, а последовательно его анализирует.

Начнет он с функции main в main.cpp, определит, что создается объект trouble типа Trouble из trouble.h, обнаружит конструктор.

Далее он сделает вывод, что по выходу из main объект trouble должен быть удален, попытается найти деструктор, не найдет его и создаст его сам.

Что будет в этом деструкторе? Естественно удаление членов класса, то есть в данном случае std::auto_ptr<Test>, в деструкторе которого будет соответственно вызван оператор delete для указателя на Test.

Внимательно следим за руками! В данном месте компилятор еще ничего не знает о типе Test, поскольку еще не дошел до test.h и поэтому оператор delete будет применен к неполному типу, что вызовет неопределенное поведение.

Откомпилировав приведенный код и выполнив программу вы скорее всего на выводе получите только сообщение из конструктора, деструктор для Test вызван не будет!

Далее подробности и методы борьбы с данным явлением.

Читать заметку полностью »

31st Март 2010
21:47

Рубрика: C++

Метки:

О копировании объектов в C++

10 комментариев »

Я регулярно сталкиваюсь с ошибками связанными с невнимательностью или незнанием механизма копирования объектов в C++. Поэтому первое правило:

Не копируйте!

Задайте себе вопрос, действительно ли класс должен поддерживать копирование? Скорее всего это не нужно как по соображениям эффективности (передавать объект по ссылке или указателю менее накладно, чем создавать его копию при передаче по значению), так и просто исходя из здравого смысла – зачем две копии объекта, представляющего к примеру базу данных с пользователями или порт? Поэтому сделайте класс некопируемым. Для этого надо перенести объявления копирующего конструктора и оператора присваивания в защищенную секцию (определять их необязательно):

class Port
{
public:
	Port();
	virtual ~Port();
 
private:
	Port(const Port&);
	Port& operator=(const Port&);
};

В этом случае при попытке скопировать объект возникнет ошибка компиляции из-за недоступности либо конструктора копирования, либо оператора присваивания.

Но чтобы каждый раз не писать данный код и дать людям ясно понять чего вы хотите, унаследуйте класс от boost::noncopyable:

#include <boost/noncopyable.hpp>
 
class Port
	: private boost::noncopyable
{
public:
	Port();
	virtual ~Port();
};

На этом можно остановиться, но если же вы решили, что класс должен поддерживать копирование, то читайте дальше.

Читать заметку полностью »

23rd Март 2010
0:18

Рубрика: C++

Метки:

Архивирование с библиотеками zlib и bzip2/libbzip2 используя Boost Iostreams

Прокомментировать »

Недавно у меня возникла задача добавить сжатие данных в программу. Сразу нашлось две свободных библиотеки – zlib и libbzip2 написанные на C. Изучив вопрос глубже, оказалось что писать удобные обертки над функциями на C не нужно, так как в Boost IOStreams все уже написано.

О том как добавить данный функционал и правильно собрать проект я расскажу в данной заметке. Также будет приведен пример кода для сжатия и распаковки файлов и сравнение zlib (алгоритм DEFLATE, методы gzip и zlib) и libbzip2 (алгоритм bzip2) по скорости работы и уровню сжатия тестовых, бинарных и исполняемых файлов.

Читать заметку полностью »

7th Март 2010
22:59

Рубрика: C++,Разработка

Метки:

Полезные переменные CMake

11 комментариев »

Крайне полезная информация по переменным CMake находится по адресу http://www.cmake.org/Wiki/CMake_Useful_Variables. После прочтения сами собой отпадают многие вопросы.

Ну и напомню, что содержимое переменной XXX можно посмотреть:

message (${XXX})

И изменить:

set (XXX ${XXX} значение)

А также проверить присутствует ли в переменной определенная подстрока:

if (XXX MATCHES "подстрока")
	...
endif ()

Если нужно изменить часть содержимого переменной (например флаг компилятора), то на помощь придут регулярные выражения:

string (REGEX REPLACE /W[0-4] /W4 CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})

В данном случае будет установлен максимальный уровень предупреждений /W4 для компилятора от Microsoft.

2nd Март 2010
23:14

Рубрика: Разработка,Сборка

Метки: