类设计者的建议

很多程序猿在进行抽象之后, 不知道该如何设计类,哪些是有用的, 哪些是可忽略的, 现提供一些设计建议, 请斟酌取之。

1、是否需要构造函数

 

class test
{
public:
	int length()
	{
		return len;
	}

private:
    int len;
};

当我们定义了一个int类型的变量, 我们如何给int变量赋初值呢, 首先:

int len = 0; // 这种方式肯定是不行的,语法规定数据成员在定义的时候不能直接初始化,那么在类里面有提供什么特殊的机制来初始化成员变量吗?

这时候,你就需要构造函数来帮你完成必要的初始化过程。

class test
{
public:
	test()
	{
		len = 0;
	}

	int length()
	{
		return len;
	}

    int len;
};
 
 

构造函数主要是提供了初始化过程和完成内存分配。

但是有一些初始化过程比较特殊, 比如const的类型的数据如何进行初始化, C++提供了冒号语法:

class test
{
public:
	test():len(0)
	{
	}

	int length() const
	{
		return len;
	}
private:
    const int len;
};


所以,构造函数取决设计者的问题复杂度,这个可以灵活把握。注意:不显示的声明一个构造函数, 编译器会给你生成一个默认的构造函数。 

2、如何设计数据成员

class test
{
public:
    int len;
};

这是一个错误的示范,如何你的类如此设计, 那么你会将隐私暴露给所有人, 就好像你不穿衣服走到大街上,简直一览无余。

C++一个重要的思想就是信息隐藏,将一些不想让别人知道的信息封装起来, 所以,好的设计应当是这样:

class test
{
public:
	test():len(0){}

	void set_length(int v) 
	{
		return len = v;
	}

	int get_length()const
	{
		return len;
	}

private:
    int len;
};

当然了,不一定所有的数据成员设计成protected活private就是好的设计, 比如一些状态,信号等可以考虑使用public,主要是与实际的使用场景有关。

3、析构函数

不是所有的类都需要析构函数,如果类并未分配资源,那么你的类就不需要析构函数。那么什么时候我需要析构呢?

如果你的类进行了资源分配,而且这些资源不会有成员函数负责回收,那么你就需要析构函数。特别是有new、malloc的场景。

class base
{
public:
	int length;
};

class deviced:public base
{
public:
	int len;
};

base *ptemp = new deviced;
delete ptemp; //编译出错

那么什么时候需要虚析构函数呢?

首先要有继承关系,其次类分配了资源, 需要回收。

4、拷贝构造函数

谈到拷贝构造, 就不得不来说说深浅拷贝的问题。

class test 
{   
public:   
	test (const char* pbuf=NULL)
	{   
		if (pbuf)
		{
			m_pbuff = new char[strlen(pbuf)+1];
			strcpy(m_pbuff, pbuf);
		}
		else
		{
			m_pbuff = NULL;
		}

		cout << "调用构造" << endl;   
	} 

	inline char * get_str(void)
	{
		return m_pbuff;
	}
	
	~test () 
	{   
		if(m_pbuff) 
		{   
			delete[] m_pbuff;   
			m_pbuff = NULL;   
		} 
		cout << "调用析构" << endl;   
	}

private:   
	char * m_pbuff;   
};   

int _tmain(int argc, _TCHAR* argv[])
{	<p>    test temp("hello world");   
    test temp1(temp);</p><p>    cout << "temp    " << temp.get_str()<< endl;   
    cout << "temp1   " << temp1.get_str()<< endl;     </p>    return 0;
}

先看一下运行结果:

先在分析一下:
 编译通过了,运行后出现一堆的错误,为什么?!这就是浅拷贝带来的问题。

       事实上在对象拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,对于类类型成员变量,调用其相应类型的拷贝构造函数。原型如下:
test(const test& str) {}
缺省拷贝构造函数在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标也就是浅拷贝。

其中temp和temp1都指向了hello word的内存空间,调用get_str()方法时,当然可以使用,但是一旦有一个对象析构之后, 就会影响其他对象使用,主要有两种情况:

1、temp析构,temp1在使用的get_str()就会出现问题, 当前m_pbuff为NULL可能不是预期的结果

2、temp析构,temp1析构时就会出现上面的错误, 因为试图去释放一个已释放的空间。

由此C++引入了拷贝构造的方法,来解决浅拷贝带来的问题。

test::test (const test &v1)
{
    m_pbuff = new char[strlen(v1.m_pbuff) +1];
    strcpy(m_pbuff, v1.m_pbuff);
}

运行结果如下:


拷贝构造函数将同一个类的不同对象之间的联系切断了, 也应当是这样, 就好比你去银行存钱,你的账户可以被其他用户使用,这样的话你还会去存钱吗?呵呵比喻不是很恰当,但是C++的哲学就是这样, 需要多思考,才能进步。

5、赋值操作符

先看一下赋值符如何定义:

test& operator=(const test& v1){}

为什么需要赋值操作符呢?编译器默认所有的赋值操作都是按字节复制的,如果类中存在指针,那不就是又陷入了深浅拷贝的圈套了, 上面已经叙述原因,这里主要说一下赋值操作符的写法:

//写法1
test& operator=(const test& v1)
{
	if (&v1 != this)
	{
		delete[] m_pbuff;

		m_pbuff = new char[strlen(v1.m_pbuff) +1];
	strcpy(m_pbuff, v1.m_pbuff);
	}
	return *this;
}

//写法2
test& operator=(const test& v1)
{
	if (&v1 != this)
	{
		char* temp = new char[strlen(v1.m_pbuff) +1]; //如果失败,由析构负责回收资源
		strcpy(temp, v1.m_pbuff);
		delete[] m_pbuff;
		m_pbuff = temp;
	}
	return *this;
}

//写法3 看网上大牛的代码, 感受颇多啊。
test& operator=(const test& v1)
{
	if (&v1 != this)
	{
		test temp(v1);
		char *temp1 = m_pbuff;
		m_pbuff = temp.m_pbuff;
		temp.m_pbuff = m_pbuff;
	}
	return *this;
}

事实上,这是借助了以上自定义的拷贝构造函数。定义了局部对象temp,在拷贝构造中已经为temp的成员指针分配了一块内存,所以只需要交换指针即可,简化了程序的设计,因为temp是局部对象,离开作用域会自动析构,避免了内存泄露,大神的思路确实不错, 值得学习和深思。

6、关系操作符

关系操作符的定义如下:

test::operator 关系符(const test& v){}

是否需要关系操作符的原则是:创建类的有序集合,就必须提供关系操作符。也就是说,类提供了比较相关的操作,比如类的两个实例进行大小比较操作等等。

7、const的使用

如果成员函数有引用参数,只有当函数想改变参数时才不需要const的来修饰,否则都需要。这不仅是一个良好的习惯, 而且会减少出错的几率。

class test 
{   
public:   
	test (const char* pbuf=NULL)
	{   
		if (pbuf)
		{
			m_pbuff = new char[strlen(pbuf)+1];
			strcpy(m_pbuff, pbuf);
		}
		else
		{
			m_pbuff = NULL;
		}

		cout << "调用构造" << endl;   
	} 

	inline char * get_str(void)
	{
		return m_pbuff;
	}
	
	~test () 
	{   
		if(m_pbuff) 
		{   
			delete m_pbuff;   
			m_pbuff = NULL;   
		} 

		cout << "调用析构" << endl;   
	}


private:   
	char * m_pbuff;   
};  

int fun(const test& v)
{
	cout << v.get_str() << endl;  //error const对象不能调用非cosnt成员函数。
}

这里需要inline char * get_str(void)改为inline char * get_str(void)const 就可以了, 也应当是这样的,关于const请看http://blog.csdn.net/Eric_Jo/article/details/4138548

这里不在啰嗦了。

8、总结

C++更适合于哪些喜欢思考的程序猿,希望能与君共勉。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值