string类的框架
# ifndef __MYSTRING__
# define __MYSTRING__
class String
{
. . .
} ;
String :: function ( . . . ) . . .
Global- function ( . . . ) . . .
# endif
string类的设计
class String
{
public :
String ( const char * cstr = 0 ) ;
String ( const String& str) ;
String& operator = ( const String& str) ;
~ String ( ) ;
char * get_c_str ( ) const { return m_data; }
private :
char * m_data;
} ;
只要类中数据有指针,一定要自定义拷贝构造函数、拷贝赋值操作符、析构函数(Big Tree,三个特殊函数) String的拷贝是深拷贝,要将指针所指的内容复制一份,同时创建一个新的指针指向它
构造函数和析构函数
inline
String :: String ( const char * cstr = 0 )
{
if ( cstr) {
m_data = new char [ strlen ( cstr) + 1 ] ;
strcpy ( m_data, cstr) ;
}
else {
m_data = new char [ 1 ] ;
* m_data = '\0' ;
}
}
inline
String :: ~ String ( )
{
delete [ ] m_data;
}
inline
String :: String ( const String& str)
{
m_data = new char [ strlen ( str. m_data) + 1 ] ;
strcpy ( m_data, str. m_data) ;
}
inline
String& String:: operator = ( const String& str)
{
if ( this != & str) {
delete [ ] m_data;
m_data = new char [ strlen ( str. m_data) + 1 ] ;
strcpy ( m_data, str. m_data) ;
}
return * this ;
}
String类的设计过程
防卫式声明 需要什么样的数据,是存字符串还是存指针 要准备哪些函数,开放给外界调用(设计接口部分) 3.1 构造函数,接受什么样的参数,是否有默认值 3.2 拷贝构造函数,参数是一个同类型的对象,是否传引用,是否加const 3.3 拷贝赋值操作符,参数是同一个类型的对象,是否传引用,是否加const,返回是否为引用(只要不是局部变量就返回引用) 3.4 析构函数 3.5 设计什么样的辅助函数,根据是否修改类内的数据决定加const与否 构造函数的实现,参数指针是否为空 4.1 如果不为空,new分配足够的空间,将数据复制到新分配的内存空间内 4.2 如果为空,new一个字符,储存字符串结束标志’\0’ 析构函数,释放new的空间 拷贝构造函数,new足够大小的空间,将数据复制到新分配的空间中 拷贝复制函数,判断是否为自赋值,如果不是则先把自己释放掉,再重新申请足够大小的空间,拷贝数据,再返回this指针所指的对象 可以将所有的函数都设置为inline,不会有什么不好的影响,编译器如果不能实现,它会自己做决定的
栈和堆
栈,是存在于某个作用域内的一个内存空间。调用函数时,函数本身就会形成一个stack用来放置它所接受的参数、返回地址以及区域性的对象。 堆,是由操作系统提供的一块全局的内存空间,可以通过new动态取得该块内存空间
class Complex { . . . } ;
. . .
Complex c3 ( 1 , 2 ) ;
int main ( )
{
Complex c1 ( 1 , 2 ) ;
Complex* p = new Complex ( 3 ) ;
static Complex c2 ( 1 , 2 ) ;
}
c1是所谓的stack object,其生命在作用域结束之时结束,会自动调用它的析构函数。这种作用域内的对象,又称为auto object,因为它会被自动清理。 当离开作用域时,c1在栈中,会被自然的释放,从堆中申请的内存p需要被手动释放掉 c2是静态对象,它的生命在作用域结束后仍然存在,直到整个程序结束。 c3是全局对象,其生命在main之前村子,在整个程序结束结束,可以视为一种static,作用域是整个程序。
new的实现
Complex* pc = new Complex ( 1 , 2 ) ;
void * mem = operator new ( sizeof ( Complex) ) ;
pc = static_cast < Complex* > ( mem) ;
pc-> Complex :: Complex ( 1 , 2 ) ;
第一步调用operator new,内部调用malloc(n),分配所需大小的内存。指针指向分配内存的起始位置。 第二部将void*指针转换为所需类型的指针 第三步在指针所指的内存中调用构造函数创建所需的对象
delete的实现
delete pc;
Complex :: ~ Complex ( pc) ;
operator delete ( pc) ;
第一步对应析构函数 第二步调用operator delete,内部调用free(ps),释放内存 在上例中一共有两个删除操作,第一次是调用析构函数释放它动态分配的内存,第二次释放动态分配ps所指的内存
new[ ]与delete[ ]要搭配使用
delete[ ]会表示要删除一个数组,要调用多次析构函数,最后再释放new[ ] 申请的空间,不会发生内存泄漏。 delete表示要删除一个元素,只会调用一次析构函数,如果申请的数组中每个元素均有一个动态分配的空间,只调用一次析构函数会导致其余元素申请的空间发生内存泄漏,而不是申请数组的空间发生内存泄漏。