《C++沉思录》:类设计者的核查表

本文的11个问题提取自《C++沉思录》第四章。所有问题的说明均为自己补充。

1 你的类需要一个构造函数吗?

——正确的定义构造函数,把握好构造函数的职能范围

有些类太简单,它们的结构就是它们的接口,所以不需要构造函数。class print{void print1(){cout<<“1″<<endl;}void print2(){cout<<“2″<<endl;}void print3(){cout<<“3″<<endl;}};有些类很复杂,构造过程就有很复杂的行为,所以必须设计一个构造函数,甚至多个重载构造函数。class string{public:string(){_buf_ptr = new char [15];}string(const string &);string(const char *);private:char *_buf_ptr;};我常常困扰在这样的问题上: “我应该把这部分操作放在构造函数中,还是另外声明一个函数单独执行?” 例如:class socket_connect{public:socket_connect();PowerOn();private:int sock_fd;};socket_connect::socket_connect(){sock_fd = socket(AF_INET,SOCK_STREAM,0);init_sockaddr_in();}void socket_connect::PowerOn(){int ret = bind(sock_fd,(const sockaddr*)&servaddr,sizeof(servaddr));PERROR(ret,”bind failed!”);ret = listen(sock_fd,BACK_LOG);PERROR(ret,”listen failed!”);}

在这里,根据我们的需求,socket本身在初始化的时候就应该开始监听了吧?所以我们完全可以把bind()和listen()放到构造函数里面去嘛!

但是我还是单独提出来了一个函数PowerOn(),这是有理由的。因为我倾向于以全局变量的方式创建socket,而且不希望在我完成一系列socket之外的初始化前,就有客户端试图连接。

嗯,听起来都很有道理。第一种做法赋予了构造函数更全面的“构造”功能;第二种做法将操作细化,可以更好地控制类的行为。

那么。构造函数究竟该执行到哪一步呢?

一种说法是:PowerOn()或者单独的init()这样的函数容易被忘记调用,我们应该让class在构造后就能愉快的跑起来!另一种声音说:太过复杂的构造函数是不可靠的!你不该过分的信任构造函数!如果你在调用构造函数时出现了问题,后果会很严重的!

目前来看。我还没有找到一个严格的限定。但是对于复杂的类来说,下面的两条一定没有错! – 空的构造函数是愚蠢的行为!你至少应该把数据赋值成0嘛 – 实现太多功能的构造函数同样是愚蠢的行为!过于复杂的构造函数会让class看起来很凌乱 这额外的一条是未经过考究的: – 如果有init()这样的一个函数作为构造函数的补充,起码应该保证当我忘记调用init()时会通过某种方式给出警告或提醒!

2 你的数据成员是私有的吗?

——pubic , private 还是 protected?

在逐渐熟练的使用C++的过程中,我越来越倾向于将所有的数据成员都隐藏起来这样的做法。看这样一个例子:

class SOURCE{public:, unsigned int,void*);inline void increase() {source_amount++;};val){source_amount -= val};unsigned int get_sleeptime() const {return sleep_time;}unsigned int get_amount() const {return source_amount;}unsigned int get_speed() const {return source_speed; }char* get_name() const { return name; }private:Mutex source_lockchar name[20];unsigned int source_amount;unsigned int sleep_time;unsigned int source_speed;};

看!我把所有的数据成员的访问权限都设定为private了! 如果你很懒。将数据成员直接暴露在public也并不是错误的做法,但是这样的话有两个致命缺点

失去了对数据成员变化的完全控制 . 我们不知道在何处,在哪里,数据成员被修改了!暴露数据成员意味着会发生原本希望进行一次++操作,却意外的被清零这样的类似问题! . 所以说,这就相当于你把root权限给了所有用户,多可怕的一件事!

不易于修改 . 假设:今天我们的需求是: 每次把这个东西+1,明天需求可能就变成了: 每次把这个东西-1! . 难道说每一次你都要在所有可能出现修改类成员的地方都把++改为–吗?这显然是不现实的! . 如果我们把数据成员隐藏,仅仅提供一个访问接口,那事情就变得简单了!我只需要修改这个函数的行为就可以了! . 在上面的例子中,我可以在接口内进行mutex_lock()和mutex_unlock()操作,修改成线程安全的自增操作!这很酷!

因此藏起来所有希望保护的数据成员可能是一个不错的习惯!(尽管定义各种代替访问行为的函数接口会增加工作量)

3 你的类需要一个无参的构造函数吗?

这个问题和问题1相近,构造函数执行到什么程度算好呢? 显然这个问题没有标准答案,决定因素是:你对这个class的设计意图!

class Example{Example(int p,int q){cout<<p+q<<endl;}};class Example_2{Expamle_2() {cout<<“nothing”<<endl;}};

如果只提供一个带参数的constructor,会有哪些损失?

Example eg;Example_2 eg_2;显然第一个式子是错误的。作为类的设计者,你了解关于这个类的一切,可能不会犯这样的错误。但是如果别人需要使用你的类呢?这样的创建对象显然会带来一些小问题。考虑Example a[100],同样的道理,只提供一个有参构造函数有时候会带来小麻烦。游手好闲会使人心智生锈

《C++沉思录》:类设计者的核查表

相关文章:

你感兴趣的文章:

标签云: