一、用内存空间换效率
1.1 allocatoe类模板
在前面简述模板=顺序容器时,就提到过,标准库中的 vector 类是通过预先分配额外内存以换取不不用每次添加元素都要重新分配内存和移动元素,而是将元素直接保存加入的预先分配的内存区域。在预先分配的内存区域添加元素,使 vector 能够高效地加入元素。在预先分配内存很大可能会造成内存浪费,因为可能会创建从不使用的空对象。另外,如果预先分配的内存必须被构造,其他类就不能使用它,以及如果必须构造预先分配的内存中的对象,就不能有基类型为没有默认构造函数的类型,否则vector 没有办法知道怎样构造这些对象。
开发者手动接管内存分配时,必须处理这两个任务。分配原始内存时,必须在该内存中构造对象;在释放该内存之前,必须保证适当地撤销这些对象。
C++ 提供下面两种方法分配和释放未构造的原始内存。
- allocator 类模板,它提供可感知类型的内存分配。这个类支持一个抽象接口,以分配内存并随后使用该内存保存对象。
- 标准库中的 operator new 和 operator delete,它们分配和释放需要大小的原始的、未类型化的内存。
allocator 类是一个模板,它提供类型化的内存分配以及对象构造与撤销。
/*定义名为a的allocator对象,可以分配内存或构造T类型的对象*/
allocator<T> a;
/*分配原始的未构造内存以保存 T 类型的 n 个对象*/
a.allocate(n)
/*释放内存,在名为 p 的 T* 指针中包含的地址处保存 T 类型的 n 个对象。
*运行调用 deallocate 之前在该内存中构造的任意对象的 destroy 是用户的责任*/
a.deallocate(p,n)
/*在 T* 指针 p 所指内存中构造一个新元素。运行 T 类型的复制构造函数用 t 初始化该对象*/
a.construct(p,t)
/*运行 T* 指针 p 所指对象的析构函数*/
a.destroy(p)
/*从迭代器 b 和 e 指出的输入范围将元素复制到从迭代器b2 开始的未构造的原始内存中。
*该函数在目的地构造元素,而不是给它们赋值。假定由 b2 指出的目的地足以保存输入范围中元素的副本*/
uninitialized_copy(b, e, b2)
/*将由迭代器 b 和 e 指出的范围中的对象初始化为 t 的副本。
*假定该范围是未构造的原始内存。使用复制构造函数构造对象*/
uninitialized_fill(b, e, t)
/*将由迭代器 b 和 e 指出的范围中至多 n 个对象初始化为t 的副本。
*假定范围至少为 n 个元素大小。使用复制构造函数构造对象*/
uninitialized_fill_n(b, e, t, n)
......
allocator 类将内存分配和对象构造分开。当 allocator 对象分配内存的时候,它分配适当大小并排列成保存给定类型对象的空间。但是,它分配的内存是未构造的,allocator 的用户必须分别 construct 和 destroy 放置在该内存中的对象。 注意,allocator 分配出来的连续的内存区域。
使用allocator类模板,需要添加#include <memory>支持,在std命名空间内,下面来简单研讨下allocator 类模板的用法:
//allocator测试
std::allocator<std::string> str_alloc;//创建一个string类型的allocator对象str_alloc
std::string* p = str_alloc.allocate(5); //分配5个string对象的内存
std::string svals[5] = {"I ","am ","a ","chinese","!"};//为了方便赋值
for (size_t i = 0; i < 5; i++)
{
str_alloc.construct(p+i,svals[i]); //构建第i个对象
}
for (size_t i = 0; i < 5; i++)
{
std::cout << *(p+i); //输出第i个对象的内容
}
std::cout << "\n";
std::string* pdel = p+5;
for (size_t i = 0; i < 5; i++)
{
/* code */
str_alloc.destroy(--pdel); //销毁对象
}
str_alloc.deallocate(p,5); //释放内存
//out log
I am a chinese!
上述代码可以看到,allocator 可以预先创建多少个对象内存,这些对象内存仅仅是明确自己的对象类型,根据对象类型用于分配内存空间。然后使用者可以在这些内存对象上构建该类型的具体对象,对这些对象给与读写操作。同样地,销毁对象和释放内存也是独立的两个步骤。
1.2 仿标准库std::vector容器的Vector类模板
std::vector容器,就是预分配模板,采用内存空间换执行效率的典型设计。现在我们采用allocator 类模板来仿造一个类std::vector容器的自定义容器Vector:
创建源文件Vector.h、Vector.cpp,建立Vector类模板:
#ifndef _VECTOR_H_
#define _VECTOR_H_
#include <memory>
template <typename T>
class Vector
{
public:
Vector(): elements(0), first_free(0), ends(0) { }
Vector(const Vector&);
Vector &operator=(const Vector&);
~Vector();
void push_back(const T&);
void pop_back(T& val);
void move_back();
std::size_t size() const; //Vector 的 size(实际使用的元素的数目)等于 first_free-elements
std::size_t capacity() const; //Vector 的 capacity(在必须重新分配 Vector 之前,可以定义的元素的总数)等于 end-elements
std::size_t freeSize() const; //自由空间(在需要重新分配之前,可以增加的元素的数目)是end-first_free
typedef T value_type;
typedef value_type* iterator;
iterator begin() const;
iterator end() const;
private:
std::pair<T*, T*> alloc_n_copy(const T *, const T *);
void reallocate(); // get more space and copy existing elements
void free();
private:
static std::allocator<T> alloc; // object to get raw memory
T* elements; // 指向数组的第一个元素
T* first_free; // 向最后一个实际元素之后的那个元素
T* ends; // 指向数组本身之后的那个元素
};
#include "Vector.cpp"
#endif //_VECTOR_H_
上述代码,每个 Vector<T> 类型定义一个 allocator<T> 类型的 static 数据成员,以便在给定类型的 Vector 中分配和构造元素。每个 Vector 对象在指定类型的内置数组中保存其元素,并维持该数组的下列三个指针:
- elements,指向数组的第一个元素。
- first_free,指向最后一个实际元素之后的那个元素。
- ends,指向数组本身之后的那个元素。
T* elements; // 指向数组的第一个元素
T* first_free; // 向最后一个实际元素之后的那个元素
T* ends; // 指向数组本身之后的那个元素
根据这些指针来确定 Vector 的大小和容量:
- Vector 的 size()(实际使用的元素的数目)等于 first_free-elements。
- Vector 的 capacity()(在必须重新分配 Vector 之前,可以定义的元素的总数)等于 end-elements。
- 自由空间freeSize()(在需要重新分配之前,可以增加的元素的数目)是end-first_free。
template <typename T>
std::size_t Vector<T>::size() const
{
std::size_t size_ = (std::size_t)(first_free-elements);
return size_;
};
template <typename T>
std::size_t Vector<T>::capacity() const
{
std::size_t capacity_ = (std::size_t)(ends-elements);
return capacity_;
}
template <typename T>
std::size_t Vector<T>::freeSize() const
{
std::size_t freeSize_ = (std::size_t)(ends-first_free);
return freeSize_;
}
类似vector,Vector 提供迭代器指向begin()和end(),Vector 的begin()就是elements指向地址,end()就是first_free指向地址:
typedef T value_type;
typedef value_type* iterator;
iterator begin() const;
iterator end() const;
//
template <typename T>
typename Vector<T>::iterator Vector<T>::begin() const
{
return elements;
}
template <typename T>
typename Vector<T>::iterator Vector<T>::end() const
{
return first_free;
}
提供push_back在容器尾端添加元素对象操作,提供pop_back和move_back从尾部删除元素对象操作,pop_back还能顺带取出最末端对象。
void push_back(const T&);
void pop_back(T& val);
void move_back();
template <typename T>
void Vector<T>::push_back(const T& t)
{
// 是否有可用剩余空间?
if (first_free == ends)
reallocate(); // 分配新空间并复制现存元素,将指针重置为指向新分配的空间
//alloc.construct(first_free++, t);
alloc.construct(first_free, t);//就请求 allocator 对象构造一个新的最后元素,construct 函数使用类型 T 的复制构造函数将 t 值复制到由 first_free 指出的元素
++first_free; //将 first_free 加 1 以指出又有一个元素在用
}
template <typename T>
void Vector<T>::pop_back(T& val)
{
if(nullptr==first_free) return;
if(first_free!=elements)
{
val = *(--first_free);
alloc.destroy(first_free); //删除了对象,但不释放空间
}else{
val = *(elements);
alloc.destroy(elements); //删除了对象,但不释放空间
first_free = nullptr;
elements = nullptr;
}
}
template <typename T>
void Vector<T>::move_back()
{
if(nullptr==first_free) return;
if(first_free!=elements)
{
alloc.destroy(--first_free); //删除了对象,但不释放空间
}else{
alloc.destroy(first_free); //删除了对象,但不释放空间
first_free = nullptr;
elements = nullptr;
}
}
首先确定是否有可用空间,如果没有,就调用 reallocate函数,reallocate 分配新空间并复制现存元素,将指针重置为指向新分配的空间。
一旦确定知道还有空间容纳新元素,它就请求 allocator 对象构造一个新的最后元素。construct 函数使用类型 T 的复制构造函数将 t 值复制到由 first_free 指出的元素,然后,将 first_free 加 1 以指出又有一个元素对象在用 。
删除对象,只是将对象析构,从预分配内存中解绑出来,不会干预到已经分配的内存。
内存空间的分配和释放,由两个私有函数来完成:
void reallocate(); // get more space and copy existing elements
void free();
template <typename T>
void Vector<T>::reallocate()
{
std::ptrdiff_t size = first_free - elements; //计算当前在用的元素数目
std::ptrdiff_t newcapacity = 2 * std::max((int)size, 1);//每次重新分配时分配两倍内存,按需要扩展
/*请求 allocator 对象来获得所需数量的空间
*如果 Vector 保存 int 值,allocate 函数调用为 newcapacity 数目的int 值分配空间;
*如果 Vector 保存 string 对象,它就为给定数目的 string对象分配空间。
*/
T* newelements = alloc.allocate(newcapacity);
//标准 copy 算法的特殊版,拷贝构造旧数据到新存储空间
std::uninitialized_copy(elements, first_free, newelements);
//一旦复制和撤销了元素,就释放原来数组所用的空间
free();
elements = newelements;//重置指针以指向新分配并初始化的数
//将 first_free 和 ends指针分别置为指向最后构造的元素之后的单元以及所分配空间末尾的下一单元.
first_free = elements + size;
ends = elements + newcapacity;
}
template <typename T>
void Vector<T>::free()
{
for (T *p = first_free; p != elements; /* empty */ )
{
alloc.destroy(--p); //删除了对象,但不释放空间
}
if (elements)//检查 elements 是否实际指向一个数组。
{
// return the memory that held the elements
alloc.deallocate(elements, ends - elements);//指向由allocate分配的空间的指针
}
}
每次重新分配时分配两倍内存(这可根据实际项目场景来设定一个策略)。函数首先计算当前在用的元素数目,将该数目翻倍,并请求 allocator 对象来获得所需数量的空间。如果 Vector 为空,就分配两个元素。
在分配内存空间时,需要明确知道模板参数的对象类型:如果 Vector 保存 int 值,allocate 函数调用为 newcapacity 数目的int 值分配空间;如果 Vector 保存 string 对象,它就为给定数目的 string对象分配空间。
std::uninitialized_copy 调用使用标准 copy 算法的特殊版本,完成拷贝构造旧数据到新存储空间。
free函数是真正释放内存空间的,for 循环对旧数组中每个对象调用 allocator 的 destroy 成员它按逆序撤销元素,从数组中最后一个元素开始,以第一个元素结束。destroy 函数运行T 类型的析构函数来释放旧元素所用的任何资源。在调用 deallocate之前,必须检查 elements 是否实际指向一个数组。
而reallocate调用 free函数类清除旧空间,一旦复制和撤销了元素,就释放原来数组所用的空间。最后,必须重置指针以指向新分配并初始化的数组。将 first_free 和 end指针分别置为指向最后构造的元素之后的单元以及所分配空间末尾的下一单 。
Vector类模板提供默认、拷贝构造函数和赋值操作符,满足类基本创建对象的行为需求。
Vector(): elements(0), first_free(0), ends(0) { }
Vector(const Vector&);
Vector &operator=(const Vector&);
~Vector();
//拷贝构造函数
template <typename T>
Vector<T>::Vector(const Vector<T> &strtmp)
{
//将形参数据拷贝给自己,std::pair<T*, T*> rsp
std::pair<T*, T*> rsp = alloc_n_copy(strtmp.begin(), strtmp.end());
//更新elements, ends,first_free
elements = rsp.first;
first_free = rsp.second;
ends = rsp.second;
}
//拷贝赋值运算符
template <typename T>
Vector<T>& Vector<T>::operator=(const Vector<T> &strtmp)
{
//防止自赋值
if (this == &strtmp)
{
return *this;
}
//将形参数据拷贝给自己,std::pair<T*, T*> rsp
std::pair<T*, T*> rsp = alloc_n_copy(strtmp.begin(), strtmp.end());
//更新elements, cap,first_free
elements = rsp.first;
first_free = rsp.second;
ends = rsp.second;
}
//析构
template <typename T>
Vector<T>::~Vector()
{
free();
}
下来就可以采用Vector类模板来实现类似std::vector容器类似的功能操作,为此调用案例中,还构建了std::vector对象来对标测试:
//
Vector<int> i_vec;
std::vector<int> i_vec_std;
i_vec.push_back(10);
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
i_vec_std.push_back(10);
std::cout << "i_vec_std.size() = " << i_vec_std.size() << "\n";
std::cout << "i_vec_std.capacity() = " << i_vec_std.capacity() << "\n";
for(int i=0; i<5; i++)
{
i_vec.push_back(i);
i_vec_std.push_back(i);
}
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
std::cout << "i_vec_std.size() = " << i_vec_std.size() << "\n";
std::cout << "i_vec_std.capacity() = " << i_vec_std.capacity() << "\n";
//move_back测试
i_vec.move_back();
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
i_vec.move_back();
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
//拷贝构造测试
Vector<int> i_vec_cpy(i_vec);
std::cout << "i_vec_cpy.size() = " << i_vec_cpy.size() << "\n";
std::cout << "i_vec_cpy.capacity() = " << i_vec_cpy.capacity() << "\n";
//赋值测试
Vector<int> i_vec_opt = i_vec;
std::cout << "i_vec_opt.size() = " << i_vec_opt.size() << "\n";
std::cout << "i_vec_opt.capacity() = " << i_vec_opt.capacity() << "\n";
//仿迭代器遍历
Vector<int>::iterator iter = i_vec.begin();
while (iter!=i_vec.end())
{
std::cout << "i_vec::iter = " << *iter << "\n";
iter++;
}
//pop_back测试
int ival = 0;
int size = (int)i_vec.size();
for(int i=0; i<size; i++)
{
i_vec.pop_back(ival);
std::cout << "ival = " << ival << "\n";
}
//out log
i_vec.size() = 1
i_vec.capacity() = 2
i_vec_std.size() = 1
i_vec_std.capacity() = 1
i_vec.size() = 6
i_vec.capacity() = 8
i_vec_std.size() = 6
i_vec_std.capacity() = 8
i_vec.size() = 5
i_vec.capacity() = 8
i_vec.size() = 4
i_vec.capacity() = 8
i_vec_cpy.size() = 4
i_vec_cpy.capacity() = 4
i_vec_opt.size() = 4
i_vec_opt.capacity() = 4
i_vec::iter = 10
i_vec::iter = 0
i_vec::iter = 1
i_vec::iter = 2
ival = 2
ival = 1
ival = 0
ival = 10
二、new和delete表达式的秘密
2.1 new、delete做了啥
开发过程中,我们大多时候是通过new表达式来分配内存及创建对象和和delete 表达式来删除对象及释放内存。
int *pi = new int(100);
delete pi; pi=nullptr;
实际上,调用new表达式,发生三个步骤。首先,该表达式调用名为 operator new 的标准
库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象;接下来,运行该类型的一个构造函数,用指定初始化式构造对象;最后,返回指向新分配并构造的对象的指针。
当使用 delete 表达式,删除动态分配对象的时候,发生两个步骤。首先,对指向的对象运行适当的析构函数;然后,通过调用名为 operator delete 的标准库函数释放该对象所用内存。
operator new 和 operator delete 函数有两个重载版本,每个版本支持相关的new 表达式和 delete 表达式:
void *operator new(size_t); // allocate an object
void *operator new[](size_t); // allocate an array
void *operator delete(void*); // free an object
void *operator delete[](void*); // free an arra
可以使用operator new 和 operator new[]函数获得未构造内存,其有点类似 allocator 类模板的allocate函数分配未构造内存一样;operator delete 函数可用于释放内存空间,类似于allocator 类模板的deallocate 函数一样。
T* pt= static_cast<T*>(operator new(sizeof(T));
T* pts static_cast<T*>(operator new[](size*sizeof(T));
operator delete(pt)
operator delete[](pts)
标准库函数 operator new 是分配内存但不初始化内存的。初始化内存另有其人,就是定位new表达式。定位new可以在已分配的原始内存中初始化一个对象,它与 new 的其他版本的不同之处在于,它不分配内存。相反,它接受指向已分配但未构造内存的指针,并在该内存中初始化一个对象。实际上,定位 new 表达式使我们能够在特定的、预分配的内存地址构造一个对象。类似类似于allocator 类模板的construct 成员函数所做的事。
new (place_address) type
new (place_address) type (initializer-list) //initializer-list-初始化列表
new (place_address) T(t);
//等价于(template <typename T> std::allocator<T> alloc)
alloc.construct (place_address, t)
但定位 new 表达式比 allocator 类的 construct 成员更灵活。定位 new 表达式初始化一个对象的时候,它可以使用任何构造函数,并直接建立对象。allocator 类模板的construct 函数总是使用复制构造函数。
当然,对值型类而言,在适当的位置直接构造对象与构造临时对象并进行复制之间没有可观察到的区别,而且性能差别基本没有意义。
但对某些类而言,使用复制构造函数是不可能的(因为复制构造函数是私有的),或者是应该避免的,在这种情况下,也许有必要使用定位 new 表达式。
既然有定位 new 表达式对标 allocator 类的 construct 成员,那就有对标allocator 类模板的destroy 函数选择,这个就是析构函数的显式调用。显式调用析构函数的效果是适当地清除对象本身。但是,并没有释放对象所占的内存,如果需要,可以重用该内存空间,释放空间需要operator delete 函数。PS:调用 operator delete 函数不会运行析构函数,它只释放指定的内存。
T *p;
//template <typename T> std::allocator<T> alloc
alloc.destroy(p)
// call the destruc
p->~T();
2.2 定义自己的 operator new/delete 的实现
由此可以看到,若自行定义一个类模板TClass,可以采用这种预定义内存的方式:通过预先分配一块原始内存以保存 T对象,也许有可能改善TClass的性能。创建新T对象的时候,可以在这个预先分配的空间中构造对象。释放T对象的时候,将它们放回预先分配对象的块中,而不是将内存真正返回给系统。
在自己的类模板中,通过定义自己的名为 operator new 和 operator delete 的成员,可以屏蔽标准库operator new 和 operator delete 函数的调用,类就可以管理用于自身类型的内存。编译器看到类类型的 new 或 delete 表达式的时候,它查看该类是否有operator new 或 operator delete 成员,如果类定义(或继承)了自己的成员new 和 delete 函数,则使用那些函数为对象分配和释放内存;否则,调用这些函数的标准库版本。
创建一个BaseObj类模板,包含 operator new 和 operator delete 的成员,并采用std::allocator来管理对象内存。operator new 和operator delete最好成对出现,如果类定义了这两个成员中的一个,它也应该定义另一个。
#ifndef _BASE_OBJ_H_
#define _BASE_OBJ_H_
#include <memory>
template <typename T>
class BaseObj
{
public:
void *operator new(std::size_t);
void operator delete(void *, std::size_t);
virtual ~BaseObj() { }
protected:
T *next;
private:
static void add_to_freelist(T*);
static std::allocator<T> alloc_mem;
static T *freeStore;
static const std::size_t chunk;
};
#include "BaseObj.cpp"
#endif //_BASE_OBJ_H_
类成员 operator new 函数具有返回类型 void* ,并接受 size_t 类型的形参。由 new 表达式用以字节计算的分配内存量初始化函数的 size_t 形参。
类成员 operator delete 函数具有返回类型 void。它可以定义为接受单个 void* 类型形参,也可以定义为接受两个形参,即 void* 和 size_t 类型。由 delete 表达式用被 delete 的指针初始化 void* 形参,该指针可以是空指针。如果提供了 size_t 形参,就由编译器用第一个形参所指对象的字节大小自动初始化 size_t 形参。
void *operator new(std::size_t);
void operator delete(void *, std::size_t);
// void *operator new[](std::size_t);
// void operator delete[](void *, std::size_t);
BaseObj类有简单的接口:它的工作只是分配和管理已分配但未构造对象的自由列表。这个类将定义一个成员 operator new,返回自由列表的下一个元素,并将该元素从自由列表中删除。当自由列表为空的时候,operator new 将分配新的原始内存。这个类还定义 operator delete,在撤销对象时将元素放回自由列表。
template <typename T>
void *BaseObj<T>::operator new(size_t sz)
{
std::cout << "BaseObj operator new func is call!\n";
// new should only be asked to build a T, not an object
// derived from T; check that right size is requested
if (sz != sizeof(T))
{
#ifdef WIN32
throw std::runtime_error("BaseObj: wrong size object in operator new");
#else
std::cout << "BaseObj: wrong size object in operator new\n";
#endif
}
if (!freeStore)
{
// the list is empty: grab a new chunk of memory
// allocate allocates chunk number of objects of type T
T * array = alloc_mem.allocate(chunk);
// now set the next pointers in each object in the allocated memory
for (size_t i = 0; i != chunk; ++i)
{
add_to_freelist(&array[i]);
}
}
T *p = freeStore;
freeStore = freeStore->BaseObj<T>::next;
return p; // constructor of T will construct the T part of the object
}
template <typename T>
void BaseObj<T>::operator delete(void *p, size_t)
{
std::cout << "BaseObj operator delete func is call!\n";
if (p != 0){
// put the "deleted" object back at head of freelist
add_to_freelist(static_cast<T*>(p));
}
}
BaseObj类的 new 和 delete 成员分别从自由列表取走对象和将对象返回到自由列表。然后我们希望为自己的类型使用自由列表分配策略的类将继承 BaseObj类,通过继承,这些类可以使用 BaseObj类的 operator new 和 operator delete 定义,以及表示自由列表所需的数据成员。因为打算将 BaseObj类作为基类,所以将给它一个 public 虚析构函数。
virtual ~BaseObj() { }
由 BaseObj类定义并被它的派生类继承的数据成员是:
- freeStore 指针,指向自由列表的表头的 static 指针。
- next、从一个 BaseObj对象指向另一个 BaseObj对象的指针。
- chunk 的成员指定每当自由列表为空时将分配的对象的数目。
- add_to_freelist 函数将对象放在自由列表。
next 指针将元素链入自由列表。从 BaseObj类派生的每个类型都包含自己的类型特定的数据,加上一个从 BaseObj基类继承的指针。每个对象具有由内存分配器使用但被继承类型自己不用的一个额外指针,对象在使用的时候,该指针无意义且不使用;对象可供使用并在自由列表中的时候,就使用 next 指针来指向下一个可用的对象。
protected:
T *next;
private:
static std::allocator<T> alloc_mem;
static T *freeStore;
static const std::size_t chunk;
由于类定义了自己的operator new 和 operator delete 的成员,如果又想使用标准库的operator new 和 operator delete 函数,那就采用全局引用“::”来强制使用,论是在类内部还是外部调用。
//
ATest *ptest = new ATest(); //调用自身的成员
//
ATest *ptest_g = ::new ATest(); //调用标准库的函数
template <typename T>
void *BaseObj<T>::operator new(size_t sz)
{
T*p = ::new T; // uses global operator new
//other code
}
2.3 让您的类按需使用自定义或是标准库的operator new/delete
使用 BaseObj类,是构建一个通过继承BaseObj类的具体业务类DeriveClass来实现的:当继承 BaseObj类的时候,用来实例化 BaseObj类的模板类型将是派生类型本身DeriveClass。DeriveClass为了重用BaseObj类的自由列表管理而继承 BaseObj 类,而BaseObj类保存了指向它管理的对象类型的一个指针,该指针的类型是指向 BaseObj 的派生类型DeriveClass的指针。
class DeriveClass: public BaseObj<DeriveClass>
{
public:
DeriveClass() : pc(NULL)
{
pc = new char[10];
};
~DeriveClass(){
if(NULL!=pc)
{
delete pc;
pc = NULL;
}
};
char *pc;
};
派生类调用测试:
// 自定义类的operator new、operator delete成员
DeriveClass *ptest = new DeriveClass();
strcpy(ptest->pc,"hello");
std::cout << "ptest->pc = " << std::string(ptest->pc) << "\n";
delete ptest;
ptest = NULL;
//标准库的operator new、operator delete 函数
DeriveClass *ptest_g = ::new DeriveClass();
strcpy(ptest_g->pc,"hello");
std::cout << "ptest_g->pc = " << std::string(ptest_g->pc) << "\n";
::delete ptest_g;
ptest_g = NULL;
//out log
BaseObj operator new func is call!
ptest->pc = hello
BaseObj operator delete func is call!
ptest_g->pc = hello
三、位域
3.1 内存可以很抠搜
如果是有做嵌入式开发,内存资源更优先,那么位域是一个节省内存资源的不错选择。位域可以声明一种特殊的类数据成员,并为数据成员指定数据位大小(一个字节8位),来保存特定的位数。位域在内存中的布局是机器相关的。位域定义如下:
//
typedef unsigned int Bit;
class MyProtocol {
Bit mode: 2; //模式00 01 10 11四种
Bit modified: 1; //读写0 1两种
Bit pgroup: 3; //组
Bit pcode: 1; //编码方式
Bit pcheck: 1; //是否校验
Bit pworld1: 6; //内容1
Bit pworld2: 6; //内容2
Bit pworld3: 6; //内容3
Bit pworld4: 6; //内容4
// ...
};
//
std::cout << "sizeof(MyProtocol) = " << sizeof(MyProtocol) << "\n";
//out log
sizeof(MyProtocol) = 4
3.1 位域也很有设计性
位域必须是整型数据类型,可以是 signed 或 unsigned。通过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域":"。通常最好将位域设为 unsigned 类型。上述代码中,将一个4字节32bit的内存,分给了9个数据成员,它们占据的数据位数被强制指定,它们占据的具体数据位置,与机器相关的,例如大小端不同。
位域的使用与类的其他数据成员相同的方式访问位域。例如,作为类的 private 成员的位域可以直接访问,而作为类的 private 成员的位域只能从成员函数的定义和类的友元访问:
//
typedef unsigned int Bit;
class MyProtocol {
public:
Bit mode: 2; //模式00 01 10 11四种
Bit modified: 1; //读写0 1两种
Bit pgroup: 3; //组
Bit pcode: 1; //编码方式
Bit pcheck: 1; //是否校验
private:
Bit pworld1: 6; //内容1
Bit pworld2: 6; //内容2
Bit pworld3: 6; //内容3
Bit pworld4: 6; //内容4
// ...
public:
void set_world1(Bit val)
{
pworld1 = val;
};
Bit get_world1()
{
return pworld1;
};
};
//
std::cout << "sizeof(MyProtocol) = " << sizeof(MyProtocol) << "\n";
MyProtocol mypr_;
mypr_.mode = 1;
std::cout << "mypr_.mode = " << mypr_.mode << "\n";
mypr_.mode = (Bit)10;//1010
std::cout << "mypr_.mode = " << mypr_.mode << "\n"; //取两位即10
mypr_.set_world1(1);
std::cout << "mypr_.get_world1 = " << mypr_.get_world1() << "\n";
mypr_.set_world1(10);//00001010
std::cout << "mypr_.get_world1 = " << mypr_.get_world1() << "\n"; //取6位即001010
//out log
mypr_.mode = 1
mypr_.mode = 2
mypr_.get_world1 = 1
mypr_.get_world1 = 10
有时为了业务理解的需要,会给这些位域成员提供枚举值,便于使用者更好了解实现逻辑,增强代码可读性。
class MyProtocol {
public:
enum { UNDEF=0, MODE1 = 0X01, MODE2 = 0X02, MODE3=0X03 }; //
Bit mode: 2; //模式00 01 10 11四种
//...
};
MyProtocol mypr_;
mypr_.mode = MyProtocol::MODE2 ;
std::cout << "mypr_.mode = " << mypr_.mode << "\n";
四、联合体
4.1 共享内存-union特性
联合union是一种特殊的类。一个union对象可以有多个数据成员,但在任何时刻,只有一个成员可以有值。当将一个值赋给union对象的一个成员的时候,其他所有都变为未定义的。为union对象分配的存储的量至少与包含其最大数据成员的一样多。像任何类一样,一个union定义了一个新的类型。
联合提供了便利的办法表示一组相互排斥的值,这些值可以是不同类型的。下面这个例子,我们可能有一个处理不同各类数值或字符数据的过程。
//
union union_value {
char c_val;
int i_val;
double d_val;
unsigned long ul_val;
};
默认情况下,union 表现得像 struct:除非另外指定,否则 union 的成员都为 public 成。当然也可以像struct一样,采用typedef来简化联合体的书写:
typedef union union_value {
char c_val;
int i_val;
double d_val;
unsigned long ul_val;
}u_val;
然后看看联合体u_val的内存大小,如下列测试代码,u_val的内存大小和它最大内存长度的成员类型"double"是一致的:
//
u_val uv_;
double dv_;
std::cout << "sizeof(u_val) = " << sizeof(u_val) << "\n";
std::cout << "sizeof(uv_) = " << sizeof(uv_) << "\n";
std::cout << "sizeof(dv_) = " << sizeof(dv_) << "\n";
//out log
sizeof(u_val) = 8
sizeof(uv_) = 8
sizeof(dv_) = 8
4.2 union并不简单
union 也可以定义成员函数,包括构造函数和析构函数。但是,union 不能作为基类使用,所以成员函数不能为虚数。另外,由于是联合体共享内存,只能在构造函数初始值设定项列表中指定联合的一个成员。
//
typedef union union_value {
//union_value() : c_val('1'),i_val(10) {}; //error
union_value() : i_val(10) {};
~union_value() {};
char c_val;
int i_val;
double d_val;
unsigned long ul_val;
}u_val;
//
u_val uv_;
std::cout << "uv_.c_val = " << uv_.c_val << "\n";
std::cout << "uv_.i_val = " << uv_.i_val << "\n";
std::cout << "uv_.d_val = " << uv_.d_val << "\n";
std::cout << "uv_.ul_val = " << uv_.ul_val << "\n";
//out log
uv_.c_val =
uv_.i_val = 10
uv_.d_val = 4.94066e-323
uv_.ul_val = 10
union 不能具有静态数据成员或引用成员,而且,union 不能具有定义了构造函数、析构函数或赋值操作符的类类型的成员。可能有些系统及编译器可以通过编译及运行,但容易引起不可预测的后果。
class Utest
{
private:
int val;
public:
Utest(/* args */);
~Utest();
Utest& operator=(const Utest&);
int getVal();
};
Utest::Utest() : val(10){};
Utest::~Utest(){};
Utest& Utest::operator=(const Utest&rhs)
{
if(this==&rhs)
return *this;
val = rhs.val;
return *this;
};
int Utest::getVal(){ return val; };
typedef union illegal_union {
illegal_union(): puc(new Utest()) {};
~illegal_union(){
if(nullptr!=puc)
{
delete puc;
puc = nullptr;
}
};
Utest uc; // error: has constructor
static int is; // error: static member
//int &rfi; // error: reference member
Utest *puc; // ok: ordinary built-in pointer type
}i_union;
//
std::cout << "sizeof(i_union) = " << sizeof(i_union) << "\n";
i_union iu_;//error 无法得到i_union默认构造函数
std::cout << "iu_.puc->getVal() = " << iu_.puc->getVal() << "\n";
std::cout << "iu_.uc.getVal() = " << iu_.uc.getVal() << "\n"; //这可能是灾难
//win out log
sizeof(i_union) = 4
iu_.puc->getVal() = 10
iu_.uc.getVal() = 1224496
//linux
test.h:63:11: 错误:有构造函数的成员‘Utest illegal_union::uc’不能用在联合中
Utest uc; // error: has constructor
^
test.h:63:11: 错误:有析构函数的成员‘Utest illegal_union::uc’不能用在联合中
test.h:63:11: 错误:有拷贝赋值运算符的成员‘Utest illegal_union::uc’不能用在联合中
test.h:63:11: 附注:unrestricted unions only available with -std=c++11 or -std=gnu++11
test.h:64:16: 错误:‘illegal_union::is’不能是静态的,因为它是联合的成员
static int is; // error: static member
所以上述代码只有指针成员是可行的:
typedef union illegal_union {
illegal_union(): puc(new Utest()) {};
~illegal_union(){
if(NULL!=puc)
{
delete puc;
puc = NULL;
}
};
// Utest uc; // error: has constructor
// static int is; // error: static member
//int &rfi; // error: reference member
Utest *puc; // ok: ordinary built-in pointer type
int uval; //OK
}i_union;
另外,可以对联合体直接赋值,就像使用成员变量一样方便,只是右值要括在一对花括号中。
//
typedef union union_value {
// union_value() : i_val(10) {};
// ~union_value() {};
char c_val;
int i_val;
double d_val;
unsigned long ul_val;
}u_val;
//记得要注释了显示定义构造函数、赋值函数哦
u_val uv_c = {'a'};
std::cout << "uv_c.c_val = " << uv_c.c_val << "\n";
uv_c = {100};
std::cout << "uv_c.i_val = " << uv_c.i_val << "\n";
//out log
uv_c.c_val = a
uv_c.i_val = 100
联合体成员的访问和struct类似,可以使用普通成员访问操作符(. 和 ->)访问 union 类型对象的成员。
typedef union illegal_union {
illegal_union(): puc(new Utest()) {};
~illegal_union(){
if(NULL!=puc)
{
delete puc;
puc = NULL;
}
};
// Utest uc; // error: has constructor
// static int is; // error: static member
//int &rfi; // error: reference member
Utest *puc; // ok: ordinary built-in pointer type
int uval; //
}i_union;
//
i_union iu_;
std::cout << "iu_.puc->getVal() = " << iu_.puc->getVal() << "\n";
std::cout << "iu_.uval = " << iu_.uval << "\n";
使用 union 对象时,要确切知道 union 对象中当前存储的是什么类型的值。通过错误的数据成员检索保存在 union 对象中的值,可能会导致程序崩溃或者其他不正确的程序行为。最好能定义一个单独的对象跟踪 union 中存储了什么值。
union 最经常用作嵌套类型,并附加一个外围成员作为union当前存储类型跟踪标记:
class UClass {
public:
enum ValType {INT, CHAR, DBL};
ValType tid;
union { // unnamed union
char cval;
int ival;
double dval;
} val; // union member val
UClass & operator=(const char &val_)
{
tid = CHAR;
val.cval = val_;
return *this;
};
UClass & operator=(const int &val_)
{
tid = INT;
val.ival = val_;
return *this;
};
UClass & operator=(const double &val_)
{
tid = DBL;
val.dval = val_;
return *this;
};
};
//
UClass uc_test;
uc_test = 100;
switch (uc_test.tid) {
case UClass::INT:
std::cout << uc_test.val.ival <<"\n"; break;
case UClass::CHAR:
std::cout << uc_test.val.cval <<"\n"; break;
case UClass::DBL:
std::cout << uc_test.val.dval <<"\n"; break;
default: break;
}
不用于定义对象的未命名 union 称为匿名联合。匿名 union 的成员的名字出现在外围作用域中。但要求匿名 union 不能有私有成员或受保护成员,也不能定义成员函数。
class UClass {
public:
enum ValType {INT, CHAR, DBL};
ValType tid;
union { // unnamed union
char cval;
int ival;
double dval;
};//不定义联合名称
UClass & operator=(const char &val_)
{
tid = CHAR;
cval = val_;
return *this;
};
UClass & operator=(const int &val_)
{
tid = INT;
ival = val_;
return *this;
};
UClass & operator=(const double &val_)
{
tid = DBL;
dval = val_;
return *this;
};
};
//
UClass uc_test;
uc_test = 100;
switch (uc_test.tid) {
case UClass::INT:
std::cout << uc_test.ival <<"\n"; break;//直接使用联合成员名称
case UClass::CHAR:
std::cout << uc_test.cval <<"\n"; break;
case UClass::DBL:
std::cout << uc_test.dval <<"\n"; break;
default: break;
}
五、源码及测试
5.1 编译测试
本博文源码由Vector.h/cpp、BaseObj.h/cpp、test.h、main.cpp六个组成,通过g++ main.cpp -o test.exe -std=c++11指令编译,运行程序如下:
5.2 全部源代码文件
main.cpp
#include "Vector.h"
#include "BaseObj.h"
#include "test.h"
#include <memory>
#include <iostream>
#include <vector>
#include <cstring>
int main(int argc, char* argv[])
{
//allocator测试
std::allocator<std::string> str_alloc;//创建一个string类型的allocator对象str_alloc
std::string* p = str_alloc.allocate(5); //分配5个对象的内存
std::string svals[5] = {"I ","am ","a ","chinese","!"};//为了方便赋值
for (size_t i = 0; i < 5; i++)
{
str_alloc.construct(p+i,svals[i]); //构建第i个对象
}
for (size_t i = 0; i < 5; i++)
{
std::cout << *(p+i); //输出第i个对象的内容
}
std::cout << "\n";
std::string* pdel = p+5;
for (size_t i = 0; i < 5; i++)
{
/* code */
str_alloc.destroy(--pdel); //销毁对象
}
str_alloc.deallocate(p,5); //释放内存
//
Vector<int> i_vec;
std::vector<int> i_vec_std;
i_vec.push_back(10);
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
i_vec_std.push_back(10);
std::cout << "i_vec_std.size() = " << i_vec_std.size() << "\n";
std::cout << "i_vec_std.capacity() = " << i_vec_std.capacity() << "\n";
for(int i=0; i<5; i++)
{
i_vec.push_back(i);
i_vec_std.push_back(i);
}
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
std::cout << "i_vec_std.size() = " << i_vec_std.size() << "\n";
std::cout << "i_vec_std.capacity() = " << i_vec_std.capacity() << "\n";
//move_back测试
i_vec.move_back();
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
i_vec.move_back();
std::cout << "i_vec.size() = " << i_vec.size() << "\n";
std::cout << "i_vec.capacity() = " << i_vec.capacity() << "\n";
//拷贝构造测试
Vector<int> i_vec_cpy(i_vec);
std::cout << "i_vec_cpy.size() = " << i_vec_cpy.size() << "\n";
std::cout << "i_vec_cpy.capacity() = " << i_vec_cpy.capacity() << "\n";
//赋值测试
Vector<int> i_vec_opt = i_vec;
std::cout << "i_vec_opt.size() = " << i_vec_opt.size() << "\n";
std::cout << "i_vec_opt.capacity() = " << i_vec_opt.capacity() << "\n";
//仿迭代器遍历
Vector<int>::iterator iter = i_vec.begin();
while (iter!=i_vec.end())
{
std::cout << "i_vec::iter = " << *iter << "\n";
iter++;
}
//pop_back测试
int ival = 0;
int size = (int)i_vec.size();
for(int i=0; i<size; i++)
{
i_vec.pop_back(ival);
std::cout << "ival = " << ival << "\n";
}
// 自定义类的operator new、operator delete成员
DeriveClass *ptest = new DeriveClass();
strcpy(ptest->pc,"hello");
std::cout << "ptest->pc = " << std::string(ptest->pc) << "\n";
delete ptest;
ptest = NULL;
//标准库的operator new、operator delete 函数
DeriveClass *ptest_g = ::new DeriveClass();
strcpy(ptest_g->pc,"hello");
std::cout << "ptest_g->pc = " << std::string(ptest_g->pc) << "\n";
::delete ptest_g;
ptest_g = NULL;
//
std::cout << "sizeof(MyProtocol) = " << sizeof(MyProtocol) << "\n";
MyProtocol mypr_;
mypr_.mode = 1;
std::cout << "mypr_.mode = " << mypr_.mode << "\n";
mypr_.mode = (Bit)10;//1010
std::cout << "mypr_.mode = " << mypr_.mode << "\n"; //取两位即10
mypr_.set_world1(1);
std::cout << "mypr_.get_world1 = " << mypr_.get_world1() << "\n";
mypr_.set_world1(10);//00001010
std::cout << "mypr_.get_world1 = " << mypr_.get_world1() << "\n"; //取6位即001010
//
mypr_.mode = MyProtocol::MODE2 ;
std::cout << "mypr_.mode = " << mypr_.mode << "\n";
//
u_val uv_;
double dv_;
std::cout << "sizeof(u_val) = " << sizeof(u_val) << "\n";
std::cout << "sizeof(uv_) = " << sizeof(uv_) << "\n";
std::cout << "sizeof(dv_) = " << sizeof(dv_) << "\n";
//
std::cout << "uv_.c_val = " << uv_.c_val << "\n";
std::cout << "uv_.i_val = " << uv_.i_val << "\n";
std::cout << "uv_.d_val = " << uv_.d_val << "\n";
std::cout << "uv_.ul_val = " << uv_.ul_val << "\n";
//
u_val uv_c = {'a'};
std::cout << "uv_c.c_val = " << uv_c.c_val << "\n";
uv_c = {100};
std::cout << "uv_c.i_val = " << uv_c.i_val << "\n";
//
std::cout << "sizeof(i_union) = " << sizeof(i_union) << "\n";
i_union iu_;//error 无法得到i_union默认构造函数
std::cout << "iu_.puc->getVal() = " << iu_.puc->getVal() << "\n";
//std::cout << "iu_.uc.getVal() = " << iu_.uc.getVal() << "\n"; //这可能是灾难
std::cout << "iu_.uval = " << iu_.uval << "\n";
//
UClass uc_test;
uc_test = 100;
switch (uc_test.tid) {
case UClass::INT:
std::cout << uc_test.val.ival <<"\n"; break;
case UClass::CHAR:
std::cout << uc_test.val.cval <<"\n"; break;
case UClass::DBL:
std::cout << uc_test.val.dval <<"\n"; break;
default: break;
}
return 0;
}
Vector.h
#ifndef _VECTOR_H_
#define _VECTOR_H_
#include <memory>
template <typename T>
class Vector
{
public:
Vector(): elements(0), first_free(0), ends(0) { }
Vector(const Vector&);
Vector &operator=(const Vector&);
~Vector();
void push_back(const T&);
void pop_back(T& val);
void move_back();
std::size_t size() const; //Vector 的 size(实际使用的元素的数目)等于 first_free-elements
std::size_t capacity() const; //Vector 的 capacity(在必须重新分配 Vector 之前,可以定义的元素的总数)等于 end-elements
std::size_t freeSize() const; //自由空间(在需要重新分配之前,可以增加的元素的数目)是end-first_free
typedef T value_type;
typedef value_type* iterator;
iterator begin() const;
iterator end() const;
private:
std::pair<T*, T*> alloc_n_copy(const T *, const T *);
void reallocate(); // get more space and copy existing elements
void free();
private:
static std::allocator<T> alloc; // object to get raw memory
T* elements; // 指向数组的第一个元素
T* first_free; // 向最后一个实际元素之后的那个元素
T* ends; // 指向数组本身之后的那个元素
};
#include "Vector.cpp"
#endif //_VECTOR_H_
Vector.cpp
#include "Vector.h"
#ifdef __linux__
#include <stdio.h>
#endif
template <typename T>
std::allocator<T> Vector<T>::alloc;
//拷贝构造函数
template <typename T>
Vector<T>::Vector(const Vector<T> &strtmp)
{
//将形参数据拷贝给自己,std::pair<T*, T*> rsp
std::pair<T*, T*> rsp = alloc_n_copy(strtmp.begin(), strtmp.end());
//更新elements, ends,first_free
elements = rsp.first;
first_free = rsp.second;
ends = rsp.second;
}
//拷贝赋值运算符
template <typename T>
Vector<T>& Vector<T>::operator=(const Vector<T> &strtmp)
{
//防止自赋值
if (this == &strtmp)
{
return *this;
}
//将形参数据拷贝给自己,std::pair<T*, T*> rsp
std::pair<T*, T*> rsp = alloc_n_copy(strtmp.begin(), strtmp.end());
//更新elements, cap,first_free
elements = rsp.first;
first_free = rsp.second;
ends = rsp.second;
}
//析构
template <typename T>
Vector<T>::~Vector()
{
free();
}
template <typename T>
void Vector<T>::push_back(const T& t)
{
// 是否有可用剩余空间?
if (first_free == ends)
reallocate(); // 分配新空间并复制现存元素,将指针重置为指向新分配的空间
//alloc.construct(first_free++, t);
alloc.construct(first_free, t);//就请求 allocator 对象构造一个新的最后元素,construct 函数使用类型 T 的复制构造函数将 t 值复制到由 first_free 指出的元素
++first_free; //将 first_free 加 1 以指出又有一个元素在用
}
template <typename T>
void Vector<T>::pop_back(T& val)
{
if(NULL==first_free) return;
if(first_free!=elements)
{
val = *(--first_free);
alloc.destroy(first_free); //删除了对象,但不释放空间
}else{
val = *(elements);
alloc.destroy(elements); //删除了对象,但不释放空间
first_free = NULL;
elements = NULL;
}
}
template <typename T>
void Vector<T>::move_back()
{
if(NULL==first_free) return;
if(first_free!=elements)
{
alloc.destroy(--first_free); //删除了对象,但不释放空间
}else{
alloc.destroy(first_free); //删除了对象,但不释放空间
first_free = NULL;
elements = NULL;
}
}
template <typename T>
void Vector<T>::reallocate()
{
std::ptrdiff_t size = first_free - elements; //计算当前在用的元素数目
std::ptrdiff_t newcapacity = 2 * std::max((int)size, 1);//每次重新分配时分配两倍内存,按需要扩展
/*请求 allocator 对象来获得所需数量的空间
*如果 Vector 保存 int 值,allocate 函数调用为 newcapacity 数目的int 值分配空间;
*如果 Vector 保存 string 对象,它就为给定数目的 string对象分配空间。
*/
T* newelements = alloc.allocate(newcapacity);
//标准 copy 算法的特殊版,拷贝构造旧数据到新存储空间
std::uninitialized_copy(elements, first_free, newelements);
//一旦复制和撤销了元素,就释放原来数组所用的空间
free();
elements = newelements;//重置指针以指向新分配并初始化的数
//将 first_free 和 ends指针分别置为指向最后构造的元素之后的单元以及所分配空间末尾的下一单元.
first_free = elements + size;
ends = elements + newcapacity;
}
template <typename T>
std::pair<T*, T*> Vector<T>::alloc_n_copy(const T *b, const T *e)
{
T* newdata = alloc.allocate(e - b);
//将原数据用来初始化新空间
T* first_free = std::uninitialized_copy(b, e, newdata);
return {newdata, first_free};
}
template <typename T>
void Vector<T>::free()
{
for (T *p = first_free; p != elements; /* empty */ )
{
alloc.destroy(--p); //删除了对象,但不释放空间
}
if (elements)//检查 elements 是否实际指向一个数组。
{
// return the memory that held the elements
alloc.deallocate(elements, ends - elements);//指向由allocate分配的空间的指针
}
}
template <typename T>
std::size_t Vector<T>::size() const
{
std::size_t size_ = (std::size_t)(first_free-elements);
return size_;
};
template <typename T>
std::size_t Vector<T>::capacity() const
{
std::size_t capacity_ = (std::size_t)(ends-elements);
return capacity_;
}
template <typename T>
std::size_t Vector<T>::freeSize() const
{
std::size_t freeSize_ = (std::size_t)(ends-first_free);
return freeSize_;
}
//
template <typename T>
typename Vector<T>::iterator Vector<T>::begin() const
{
return elements;
}
template <typename T>
typename Vector<T>::iterator Vector<T>::end() const
{
return first_free;
}
BaseObj.h
#ifndef _BASE_OBJ_H_
#define _BASE_OBJ_H_
#include <memory>
template <typename T>
class BaseObj
{
public:
void *operator new(std::size_t);
void operator delete(void *, std::size_t);
// void *operator new[](std::size_t);
// void operator delete[](void *, std::size_t);
virtual ~BaseObj() { }
protected:
T *next;
private:
static void add_to_freelist(T*);
static std::allocator<T> alloc_mem;
static T *freeStore;
static const std::size_t chunk;
};
#include "BaseObj.cpp"
#endif //_BASE_OBJ_H_
BaseObj.cpp
#include "BaseObj.h"
#include <iostream>
#ifdef __linux__
#include <stdio.h>
#include <exception>
#endif
//
template <typename T>
std::allocator<T> BaseObj<T>::alloc_mem;
template <typename T>
T *BaseObj<T>::freeStore = 0;
template <typename T>
const std::size_t BaseObj<T>::chunk = 24;
//operator new 使用这个函数将新分配的对象放到自由列表,删除对象
template <typename T>
void *BaseObj<T>::operator new(size_t sz)
{
std::cout << "BaseObj operator new func is call!\n";
// new should only be asked to build a T, not an object
// derived from T; check that right size is requested
if (sz != sizeof(T))
{
#ifdef WIN32
throw std::runtime_error("BaseObj: wrong size object in operator new");
#else
std::cout << "BaseObj: wrong size object in operator new\n";
#endif
}
if (!freeStore)
{
// the list is empty: grab a new chunk of memory
// allocate allocates chunk number of objects of type T
T * array = alloc_mem.allocate(chunk);
// now set the next pointers in each object in the allocated memory
for (size_t i = 0; i != chunk; ++i)
{
add_to_freelist(&array[i]);
}
}
T *p = freeStore;
freeStore = freeStore->BaseObj<T>::next;
return p; // constructor of T will construct the T part of the object
}
//operator delete 也使用该函数将对象放回自由列表
template <typename T>
void BaseObj<T>::operator delete(void *p, size_t)
{
std::cout << "BaseObj operator delete func is call!\n";
if (p != 0){
// put the "deleted" object back at head of freelist
add_to_freelist(static_cast<T*>(p));
}
}
template <typename T>
void BaseObj<T>::add_to_freelist(T *p)
{
p->BaseObj<T>::next = freeStore;
freeStore = p;
}
test.h
#ifndef _TEST_H_
#define _TEST_H_
#include "BaseObj.h"
class DeriveClass: public BaseObj<DeriveClass>
{
public:
DeriveClass() : pc(NULL)
{
pc = new char[10];
};
~DeriveClass(){
if(NULL!=pc)
{
delete pc;
pc = NULL;
}
};
char *pc;
};
//
typedef unsigned int Bit;
class MyProtocol {
public:
enum { UNDEF=0, MODE1 = 0X01, MODE2 = 0X02, MODE3=0X03 }; //
Bit mode: 2; //模式00 01 10 11四种
Bit modified: 1; //读写0 1两种
Bit pgroup: 3; //组
Bit pcode: 1; //编码方式
Bit pcheck: 1; //是否校验
private:
Bit pworld1: 6; //内容1
Bit pworld2: 6; //内容2
Bit pworld3: 6; //内容3
Bit pworld4: 6; //内容4
// ...
public:
void set_world1(Bit val)
{
pworld1 = val;
};
Bit get_world1()
{
return pworld1;
};
};
//
typedef union union_value {
// union_value() : i_val(10) {};
// ~union_value() {};
char c_val;
int i_val;
double d_val;
unsigned long ul_val;
}u_val;
class Utest
{
private:
int val;
public:
Utest(/* args */);
~Utest();
Utest& operator=(const Utest&);
int getVal();
};
Utest::Utest() : val(10){};
Utest::~Utest(){};
Utest& Utest::operator=(const Utest&rhs)
{
if(this==&rhs)
return *this;
val = rhs.val;
return *this;
};
int Utest::getVal(){ return val; };
typedef union illegal_union {
illegal_union(): puc(new Utest()) {};
~illegal_union(){
if(NULL!=puc)
{
delete puc;
puc = NULL;
}
};
// Utest uc; // error: has constructor
// static int is; // error: static member
//int &rfi; // error: reference member
Utest *puc; // ok: ordinary built-in pointer type
int uval; //
}i_union;
class UClass {
public:
enum ValType {INT, CHAR, DBL};
ValType tid;
union { // unnamed union
char cval;
int ival;
double dval;
} val; // member val is a union of the 3 listed types
UClass & operator=(const char &val_)
{
tid = CHAR;
val.cval = val_;
return *this;
};
UClass & operator=(const int &val_)
{
tid = INT;
val.ival = val_;
return *this;
};
UClass & operator=(const double &val_)
{
tid = DBL;
val.dval = val_;
return *this;
};
};
//#include "test.cpp"
#endif //_TEST_H_