C++之父力作学习笔记(4)——类的好多事
类,这个概念比较大。包含的事太多。咱们就一一的尽量弄清楚它。
一个类就是一个用户定义类型。C++里类概念的目标就是为程序员提供一种建立新类型的工具,是这些新类型的使用能够像内部一样方便。
访问控制:class成员的默认访问方式是私有的。一个struct也是一个class,但是其成员的默认方式是公用的。非成员函数禁止访问私有成员。
构造函数:就是函数名和类名一样的函数且没有返回值。这谁都知道。It's easy。而默认构造函数就是调用时不必提供参数的构造函数。如果用户自己声明了一个默认构造函数,那么就会去使用它;否则,如果有必要,而且用户没有声明其他的构造函数,编译器就会设法去生成一个。编译器生成的默认构造函数将隐式地为类类型的成员和它的基类调用有关的默认构造函数。这里解释一下:类类型(Class type)即指那些由程序员定义的类而产生的类型,以便与内部类型和其他用户定义类型相区分。相信大家这里也没什么问题。有一个注意点来了,由于const和引用必须进行初始化,包含const或引用成员的类就不能进行默认构造,除非程序员的我们自己显示的提供默认构造函数。例如:struct X
{
const int a;
const int& r;
};
X x;//错误;X无默认构造函数 默认构造函数也可以显示调用。内部类型同样也有默认构造函数。
下面再谈谈复制构造函数,先看看复制构造函数是怎么引进来的。
按照默认约定,类对象可以复制。特别是可以用一个类的对象和复制对该类的其他对象进行初始化。即使是声明了构造函数的地方,也是可以这样做:Date d=today;//通过复制初始化按照默认方式,类对象的复制就是其中各个成员的复制。如果某个类X所需要的不是这种默认方式,那么就可以定义一个复制构造函数X::X(const X&),由它提供所需要的行为。还有一个概念就是复制赋值,很容易和复制构造函数搞混。咱们就一起搞清楚它们。先看一段程序:void h()
{
Table t1;
Table t2=t1;//复制初始化
Table t3;
t3=t2; //复制赋值
}看似好像没什么问题,对于复制上面提到的解释方式,在应用到具有指针成员的类的对象时,就可能产生一种出人意料的作用。对于包含了由构造函数/析构函数管理的资源的对象而言,按成员复制的语义通常是不正确的。在这里,Table的默认构造函数为t1和t3各调用了一次,一共是两次。然而Table的析构函数则被调用了三次;对t1、t2和t3各一次!由于赋值的默认解释是按成员赋值,所以在h()结束时,t1、t2和t3中将各包含一个指针,它们都指向建立t1时从自由存储中分配的那个名字数组。在建立t3时所分配的数组的指针并没有保留下来,因为它被赋值t3=t2覆盖掉了。这样,如果没有自动废料收集,对这个程序而言,该数组的存储就将永远丢掉了。而在另一方面,为t1的创建而分配的数组因为同时出现在t1、t2和t3里,将被删除3次。这种情况所导致的结果是无定义,很可能是灾难性的。这类反常情况可以避免,方式就是将Table复制的意义定义清楚:class Table
{
//---
Table(const Table&);//复制构造函数
Table& operator=(const Table&);//复制赋值
};咱们自己可以为这些复制操作定义自己认为最合适的任何意义,例如
//这里补上Table类的详细定义
class Table
{
Name* p;
size_t sz;
public:
Table(size_t s=15)
{
p=new Name[sz=s];
}
~Table()
{
delete[] p;
}
Name* loopup(const char*);
bool insert(Name*);
}
Table::Table(const Table& t)//复制构造函数
{
p=new Name[z=t.sz];
for(int i=0;i<sz;i++)
p[i]=t.p[i];
}
Table& Table::operator=(const Table& t)//赋值
{
if(this!=&t)//当心自赋值:t=t
{
delete[] p;
p=new Name[sz=t.sz];
for(int i=0;i<sz;i++)
p[i]=t.p[i];
}
return *this;
}情况几乎总是如此,复制构造函数与复制赋值通常都很不一样。究其根本原因,复制构造函数是去完成对为初始化的存储区的初始化,而复制赋值运算符则必须正确处理一个结构良好的对象。
成员常量:
对那些静态整型成员,可以给它的成员声明加上一个常量表达式作为初始式,例如
class Curious
{
static const int c1=7;//ok,但要记得去定义
static int c2=11;//错误:非const
const int c3;//错误:非Static
static const int c4=f(1);//错误:在类里的初始表达式不是常量
static const float c5=7.0;//错误:在类里初始化的不是整型
}1)在类中不能用const来创建常量!因为:类只是描述了对象的形式,并没有真正创建对象!所以, 在对象建立之前,并没有存值空间!
2)而const是用来创建常量的! 方法1 你可以用枚举:
class a
{
enum{buf_size_t buf_size=、、、}//用枚举创建一个常量,但不是数据成员
}
方法2 你可以用static
class a
{
private:
static const buf_size_t buf_size=30;//该常量将与憋得静态常量存储在一起,而不是存储在对象中
}但《C++程序设计语言》书上说当你用到某个被初始化的成员,而且需要将它作为对象存入存储器时,这个成员就必须在某处有定义。初始式不必重复写:const int Curious::c1;//必须,但这里不必重复初始式
const int* p=&Cusious::c1;//ok:Curious::c1已经有定义这里有点让我懵了,为什么还要const int Curious::c1;//必须,但这里不必重复初始式 这一行呢?还说是必须,经过测试是有问题的——当前范围内的定义或重新声明非法,到底是书错了还是还有其他什么原因?
还请高手不吝赐教
今天就到这里吧,到这里就出了问题,还需要思考。
补充:软件开发 , C++ ,