Собственно вопрос: как протестировать класс на корректность работы самописаных конструктора копирования и оператора присваивания, и на предмет их существования вообще.
Положим, что в конструкторе класса создаётся динамический объект, а в деструкторе удаляется.
Если свои конструктора копирования и оператора присваивания не определять, то компилятор будет использовать побитовое копирование.
При этом могут возникать коварные ситуации. Как я понимаю, связанные с двойным освобождением ресурсов.
Можно ли написать код, который бы осуществлял подобный тест?
http://www.devexp.ru/2010/03/o-kopirovanii-obektov-v-c/
вот тут кое-что интересное о копировании объектов и присваивании.
я пока примитивный тест сделал (мой класс - простенький буфер):
1) проверяем правильность копирования (идентичность копии)
2) модифицируем исходный буфер, новый не должен поменяться
3) модифицируем новый буфер, исходный не должен поменяться
Если я закоментирую оператор присваивания и конструктор копирования, два последних теста не проходят (как и ожидалось).
Код весь находится в функции main и выглядит примерно так:
// тестируем конструктор копирования
{
// инициализируем буфер масивом
YBuffer1 buffer(rowBuffer, SIZE);
YBuffer1 bufferCopy(buffer);
// проверяем правильность копирования
ok = true;
for (i = 0; i < SIZE; i++){
if (bufferCopy[i] != buffer.at(i)){
ok = false;
}
}
if (ok){
qDebug() << "test COPY CONSTRUCTOR, coping - passed";
}else{
qDebug() << "test COPY CONSTRUCTOR, coping - ERROR";
}
const unsigned int index1 = SIZE/2;
// модифицируем исходный, новый не должен поменяться
char originalFirst = buffer.at(index1);
buffer[index1] = ~buffer.at(index1);
if (bufferCopy.at(index1) == originalFirst){
qDebug() << "test COPY CONSTRUCTOR, modifying original - passed";
}else{
qDebug() << "test COPY CONSTRUCTOR, modifying original - ERROR";
}
// модифицируем новый, исходный не должен поменяться
const unsigned int index2 = index1/2;
char originalSecond = buffer.at(index2);
bufferCopy[index2] = ~bufferCopy.at(index2);
if (buffer.at(index2) == originalSecond){
qDebug() << "test COPY CONSTRUCTOR, modifying new - passed";
}else{
qDebug() << "test COPY CONSTRUCTOR, modifying new - ERROR";
}
}// конец теста
...
//ещё несколько тестов
Т.е. я ограничил область видимости каждого теста фигурными скобками, и полагал, что по выходу за эти скобки, программа должна аварийно завершатся при отсутствии собственных конструктора копирования и оператора присваивания. Но программа падает почему-то после всех тестов, а не когда произойдёт выход за фигурные скобки.
Гмм.... А можно взглянуть на код этого YBuffer1?
template<typename T>
void TestCopyAndAssign(const T & obj) {
T copy(obj);
assert((copy == obj) && "Testing copy failed");
T assign;
assign = obj;
assert((assign == obj) && "Testing assign failed");
}
class A {
public:
B *b;
//... all operators
bool operator==(const A &obj) {
assert((this->b != obj.b) && "Shit happened, object copying or assigning doesn't handle pointers");
return *this->b == *obj.b;
}
}
#ifndef YBUFFER1
#define YBUFFER1
/** Класс YBuffer1 является ... .
* Этот класс ...
*/
class YBuffer1
{
public:
YBuffer1(const char *abuffer, unsigned int asize);
~YBuffer1();
YBuffer1(const YBuffer1 &other);
YBuffer1 &operator=(const YBuffer1 &other);
const char& at(unsigned int i) const;
char& operator[](unsigned int i);
char operator[](unsigned int i) const;
unsigned int size() const {return theSize;}
private:
unsigned int theSize;
char *theBuffer;
private:
void copy(const YBuffer1 &source, YBuffer1 &destination);
};
#endif //YBUFFER1
#include "ybuffer1.h"
#include <QtGlobal>
YBuffer1::YBuffer1(const char *abuffer, unsigned int asize)
{
theSize = asize;
theBuffer = new char[asize];
// тут надо скопировать содержимое
unsigned int cnt = 0;
while(cnt != asize){
theBuffer[cnt] = abuffer[cnt];
cnt++;
}
}
YBuffer1::~YBuffer1()
{
delete theBuffer;
}
YBuffer1::YBuffer1(const YBuffer1 &other)
{
theSize = other.size();
theBuffer = new char[other.size()];
// тут надо скопировать содержимое
copy(other, *this);
}
YBuffer1& YBuffer1::operator=(const YBuffer1 &other)
{
theSize = other.size();
theBuffer = new char[other.size()];
// тут надо скопировать содержимое
copy(other, *this);
return *this;
}
void YBuffer1::copy(const YBuffer1 &source, YBuffer1 &destination)
{
unsigned int size = source.size();
Q_ASSERT_X(size == destination.size(), "YBuffer1::copy()", "size of objects not eqal");
unsigned int cnt = 0;
while(cnt != size){
destination[cnt] = source[cnt];
cnt++;
}
}
const char& YBuffer1::at(unsigned int i) const
{
Q_ASSERT_X(i <= theSize, "YBuffer1::at()", "requested index too big");
const char &data = theBuffer[i];
return data;
}
char& YBuffer1::operator[](unsigned int i)
{
Q_ASSERT_X(i <= theSize, "YBuffer1::operator[]", "requested index too big");
char &data = theBuffer[i];
return data;
}
char YBuffer1::operator[](unsigned int i) const
{
Q_ASSERT_X(i <= theSize, "YBuffer1::operator[]", "requested index too big");
char data = theBuffer[i];
return data;
}
Я немного подправил код.... Правда, не компилировал! Только то, что бросается в глаза при чтении.
#include "ybuffer1.h"
#include <QtGlobal>
YBuffer1::YBuffer1(const char *abuffer, unsigned int asize)
{
theSize = asize;
theBuffer = new char[asize];
// тут надо скопировать содержимое
// поскольку abuffer - POD, то можно не делать цикл, а тупо использовать memcpy
// или использовать std::copy
memcpy(theBuffer, abuffer, asize);
// хотя можно использовать и цикл... дело вкуса, но практически всегда memcpy выполняется
// быстрее.
/*
unsigned int cnt = 0;
while(cnt != asize){
theBuffer[cnt] = abuffer[cnt];
cnt++;
}
*/
}
YBuffer1::~YBuffer1()
{
// эта ошибка приводит к UB! Стандарт, 5.3.5/2
/* delete theBuffer; */
delete[] theBuffer;
}
YBuffer1::YBuffer1(const YBuffer1 &other)
{
theSize = other.size();
theBuffer = new char[other.size()];
// тут надо скопировать содержимое
copy(other, *this);
}
YBuffer1& YBuffer1::operator=(const YBuffer1 &other)
{
// 1. проверить присваивание самому себе, типа i = i;
if (this != &other)
{
// 2. перед выделением нового буфера надо удалить старый.
// Еще лучше - использовать технику, безопасную по отношению к исключениям,
// см. Саттер, "Решение сложных задач на C++"
delete[] theBuffer;
theBuffer = new char[other.size()];
// тут надо скопировать содержимое
copy(other, *this);
theSize = other.size();
}
return *this;
}
void YBuffer1::copy(const YBuffer1 &source, YBuffer1 &destination)
{
unsigned int size = source.size();
Q_ASSERT_X(size == destination.size(), "YBuffer1::copy()", "size of objects not eqal");
memcpy(destination.theBuffer, source.theBuffer, source.size());
/*
unsigned int cnt = 0;
while(cnt != size){
destination[cnt] = source[cnt];
cnt++;
}
*/
}
// здесь везде i должны быть строго < theSize!
const char& YBuffer1::at(unsigned int i) const
{
Q_ASSERT_X(i < theSize, "YBuffer1::at()", "requested index too big");
return theBuffer[i];
}
char& YBuffer1::operator[](unsigned int i)
{
Q_ASSERT_X(i < theSize, "YBuffer1::operator[]", "requested index too big");
return theBuffer[i];
}
char YBuffer1::operator[](unsigned int i) const
{
Q_ASSERT_X(i < theSize, "YBuffer1::operator[]", "requested index too big");
return theBuffer[i];
}
POD - Plain Old Data.
Юрий, просто Стандарт языка (п. 5.3.5 положение (clause) 2) четко описывает требования к выделению и удалению памяти:
- если память была выделена по new для единичного объекта (неважно, какого типа) - то удаляться она должна только delete для единичного объекта;
- если память была выделена по new[] для массива объектов (array form) - то удаляться она должна только delete[] для массива объектов (тоже array form) и никак иначе.
Иначе поведение программы не определено (Otherwise behavior is undefined, если мой склероз мне не изменяет). Причем, Стандарт никак не конкретизирует это "неопределенное поведение" - программе разрешается делать все, что ей заблагорассудится, начиная от порчи памяти и невоспроизводимых глюков, и вплоть до format C:.
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)