类的基本结构
2006-01-26 14:43
204 查看
今天早上看了一本数据结构的书关于类和动态存储的章节。书里面大概地举了几个关于在一个类里面动态申请内存的例子。我由此想到就算是一个一般的类也应该具备一定的基本结构,而并不不是一个类头、几个数据成员和成员函数就可以作为一个类“安全”地使用。
我们先看一些例子。
以前,我看到过有很多人这样写一个类:
class A
{
public:
int GetCount(){return ++m_count;}
private:
int m_count;
};
编写者目的是获得A类对象调用成员函数GetCount的次数。
结果是获得一个很小的负数!当然这是一个很低级的错误,现在已经没有谁会范的了。
哈哈,所以我的第一个结论是:一个类应该有一个用户自定义的默认构造函数。另外,在我的VC6.0编译器中,如果一个类有多个构造函数(但是没有一个默认构造函数,即没有一个不带参数的构造函数),它是不会为你自动加上去的,所以自定义的默认构着函数应该是每一个类都应该有的。
于是改为:
class A
{
public:
A() {m_count = 0;}
int GetCount(){return ++m_count;}
private:
int m_count;
};
错误就消失了。
但是加了一些功能以后,另一个错误又出现了。
class B
{
public:
B(){m_count = 0; m_pstr = 0x0;}
B(const string &str);
int GetInt() {return m_count;}
private:
int m_count;
string *m_pstr;
};
//////////////////////////////////////////////////////////////////////////
B::B(const string &str) {
m_count = 0;
m_pstr = new string(str);
}
如果有一下语句:
//这条B obj1(“string”)语句在VC6.0中不会出现错误,但在C++Builder中就会出现错误,具、、//体原因可以查看具体编译器的帮助文档。本文最后也概略讲一下。不同编译器有不同的实//现。
B obj1(“string”),obj2 = obj1;
以上语句只是告诉编译器要吧obj1的信息安位拷贝到obj2中。结果执行语句后,obj2的m_pstr和obj1的m_pstr成员指向同一块内存地址。显然这不是我们想要的结果。如果在较复杂的程序中使用就会埋下隐蔽的祸患。
所以我的第二个结论是:一个类应该有一个复制构造函数详细地告诉编译器如何复制(初始化)一个自定义对象。这样才能使用户自定义类型使用起来能够更加“像”C++内部类型,这也许是C++“美丽“的地方之一吧^_^。
哈哈!是不是就完美无缺呢?Of course not.我们还漏了释放B类型中动态创建的对象*m_pstr。这个工作要在析构函数中完成。
于是又修改为
class B
{
public:
B(){m_count = 0; m_pstr = 0x0;}
B(const string &str);
B(const B &obj);
virtual ~B();
int GetInt() {return m_count;}
private:
int m_count;
string *m_pstr;
};
//////////////////////////////////////////////////////////////////////////
B::B(const string &str) {
m_count = 0;
m_pstr = new string(str);
}
B::B(const B &obj) {
m_count = 0;
m_pstr = new string(*obj.m_pstr);
}
B::~B(){
delete m_pstr;
m_pstr = 0x0;
}
其实,我觉得最好是把复制构造函数的孪生子妹operator=函数写上。这样能够使类无论是初始化还是赋值的时候能够正确执行。特别是上面举的有动态内存分配的情况下,就更加应该有这个重载函数。否则当对象作为形式参数传递给函数的时候就有可能出错。就如下面的情况:
int main(void) {
{
B obj0;
{
B obj1 = obj0;
}
}//在这里将出现运行期的错误!因为obj0中的*m_pstr已经在这之前被释放掉。
}
这是我一点心得。
语句
B obj1(“string”);
在VC6.0中等价于
B obj1(string(“string”));
VC6.0自动用串”string”生成一个string类型的临时对象并传递给B的构造函数。
但是在C++Builder中没有这种功能。
我们先看一些例子。
以前,我看到过有很多人这样写一个类:
class A
{
public:
int GetCount(){return ++m_count;}
private:
int m_count;
};
编写者目的是获得A类对象调用成员函数GetCount的次数。
结果是获得一个很小的负数!当然这是一个很低级的错误,现在已经没有谁会范的了。
哈哈,所以我的第一个结论是:一个类应该有一个用户自定义的默认构造函数。另外,在我的VC6.0编译器中,如果一个类有多个构造函数(但是没有一个默认构造函数,即没有一个不带参数的构造函数),它是不会为你自动加上去的,所以自定义的默认构着函数应该是每一个类都应该有的。
于是改为:
class A
{
public:
A() {m_count = 0;}
int GetCount(){return ++m_count;}
private:
int m_count;
};
错误就消失了。
但是加了一些功能以后,另一个错误又出现了。
class B
{
public:
B(){m_count = 0; m_pstr = 0x0;}
B(const string &str);
int GetInt() {return m_count;}
private:
int m_count;
string *m_pstr;
};
//////////////////////////////////////////////////////////////////////////
B::B(const string &str) {
m_count = 0;
m_pstr = new string(str);
}
如果有一下语句:
//这条B obj1(“string”)语句在VC6.0中不会出现错误,但在C++Builder中就会出现错误,具、、//体原因可以查看具体编译器的帮助文档。本文最后也概略讲一下。不同编译器有不同的实//现。
B obj1(“string”),obj2 = obj1;
以上语句只是告诉编译器要吧obj1的信息安位拷贝到obj2中。结果执行语句后,obj2的m_pstr和obj1的m_pstr成员指向同一块内存地址。显然这不是我们想要的结果。如果在较复杂的程序中使用就会埋下隐蔽的祸患。
所以我的第二个结论是:一个类应该有一个复制构造函数详细地告诉编译器如何复制(初始化)一个自定义对象。这样才能使用户自定义类型使用起来能够更加“像”C++内部类型,这也许是C++“美丽“的地方之一吧^_^。
哈哈!是不是就完美无缺呢?Of course not.我们还漏了释放B类型中动态创建的对象*m_pstr。这个工作要在析构函数中完成。
于是又修改为
class B
{
public:
B(){m_count = 0; m_pstr = 0x0;}
B(const string &str);
B(const B &obj);
virtual ~B();
int GetInt() {return m_count;}
private:
int m_count;
string *m_pstr;
};
//////////////////////////////////////////////////////////////////////////
B::B(const string &str) {
m_count = 0;
m_pstr = new string(str);
}
B::B(const B &obj) {
m_count = 0;
m_pstr = new string(*obj.m_pstr);
}
B::~B(){
delete m_pstr;
m_pstr = 0x0;
}
其实,我觉得最好是把复制构造函数的孪生子妹operator=函数写上。这样能够使类无论是初始化还是赋值的时候能够正确执行。特别是上面举的有动态内存分配的情况下,就更加应该有这个重载函数。否则当对象作为形式参数传递给函数的时候就有可能出错。就如下面的情况:
int main(void) {
{
B obj0;
{
B obj1 = obj0;
}
}//在这里将出现运行期的错误!因为obj0中的*m_pstr已经在这之前被释放掉。
}
这是我一点心得。
语句
B obj1(“string”);
在VC6.0中等价于
B obj1(string(“string”));
VC6.0自动用串”string”生成一个string类型的临时对象并传递给B的构造函数。
但是在C++Builder中没有这种功能。
相关文章推荐
- HTML概述及其基本结构
- SSRS 2012 报表基本结构与设置
- 数据基本类型,符合数据类型,数据结构的理解,抽象数据结构
- 前言:O3D程序基本结构
- PE文件结构详解(一)基本概念
- 第一,二章:微处理器基本结构
- 【Linux 入门笔记】文件系统基本结构和操作
- Html文件基本结构
- PL/SQL的基本写法、BEGIN_END块结构及简单的事务实现
- 初探HTML5框架的基本结构
- JVM学习笔记(一)------基本结构
- 无锁数据结构(基础篇):原子核、原子性、基本类型
- 数据结构——线性表——散列存储结构——哈希表知识点总结 原创 2017年05月14日 10:08:40 散列(hashing)是一种重要的存储方法,也是一种常见的查找方法。 基本思想:以结点的
- 数据结构.二叉树的基本操作(C语言实现)
- PureMVC Java 版本的基本结构
- Lua的基本语法结构
- NT式驱动的基本结构
- html和CSS学习笔记(2):标签和html文件基本结构
- avi格式的基本结构
- html基本结构标签