复制内容到剪贴板
代码:
最近我们学习VC++了,不过,一位前辈说先把C++学好比较好.于是我又去学C++.C++原先叫做"带类的C"(c with class),可见类在C++中占有很重要的地位.加上我又有些C语言的基础,(其实有人说,它们是完全不同的两种语言)所以决定从类开始学起.
我看了一下参考书,除了和C相似的那些以外,可以分成这么几个部分:一.类,包括相关的许多东西;二.指针,包括容器等相关;三.算法,包括相关算法;四.高级应用,包括模板及相关.
要说的是,其实这个笔记没有太多自己的东西在里边,基本上是把<C++ primer>(中文第四版)第十二章和第十三章以及相关章节抄一遍而已.但是包含了自己的理解在里边,而且我想,抄一遍也许有意义,会理解它更深一些.如此,必然有许多错误在,希望各位发现了可以指给我.谢.
一个完整的类类型在定义中包含了构造函数,复制控制,成员函数和成员数据.其中复制控制包括构造函数,赋值函数和析构函数.简单一些的话,可以只要成员函数和成员数据.当然,最简单的是什么也没有的空类,但它没有什么实际用处.只包含成员函数和成员数据的类如下:
class Sales_item
{
public:
double avg_price() const;
bool same_isbn(const Sales_item &rhs) const
{
return isbn==rhs.isbn;
}
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
double Sales_item::avg_price() const
{
if(units_sold)
return revenue/units_sold;
else
return 0;
}
首先说一下,C语言中有一个结构,用关键字struct,在C++中,它和class是一样的,不同之处在于,如果用struct关键字表示类,则默认成员是公有的,而class则是私有的.
因为默认是私有的,所以开始有一个public关键字,类成员函数在类体中定义和之外定义并没有不同,当然会和inline有些关系,我是说,功能并没有不同.当然,其中会有一个分号结束符和一个作用域操作符,以及在声明时和定义时都要有const关键字,这些细节也要注意.
把函数定义为const的意思是它不能改变它的数据,当然,后面会说一个mutable关键字,会有不同.这儿声明一下,这个笔记名义上是要和大家交流一下,其实还是我一个人的笔记,所以对我来说,如果遇到对我来说的新东西,我就会说一下,如果是以前就明白的,我就会略过了,所以如果你觉得它比较省略或是啰嗦,包涵.
下面说构造函数,构造函数就是当定义一个类对象时,给其中的成员数据的初使值.比如在上面的定义中的public中加上一行:Sales_item(): units_sold(0),revenue(0.0){}.类的构造函数必须和类同名,后面有一冒号,接下来是对成员数据的初使化,没有初使化string类型的isbn是因为类类型的数据有它自己的构造函数.其实如果不定义构造函数,编译器会自动合成一个,但是合成的构造函数不会初使化局部作用域的内置类型数据,所以,大多数情况下还是自己定义一个构造函数比较好.另外,构造函数可以重载,由实参决定使用哪个.对于const函数,不用const型的构造函数,普通的可以.在构造函数体中对数据赋值同样也可以初使化,不同的是,如果数据是const型的,就必须在初使化列表中进行初使化.还有一个要说的是,如果定义了一个构造函数,就必须同时定义一个默认的无参构造函数,因为那时编译器不会再自动定义了.
还有函数的重载和inline函数,没有什么要说的.
接下来是隐含的*this指针.再看另一个类:
class Screen
{
public: //something.....
private:
string contents;
string::size_type cursor;
string::size_type height,width;
};
我们想有一个set操作和一个move操作,前者表示将特定字符写到光标指定处,后者表示将光标移到新位置.我们可能会希望这么来使用这两个函数(Screen myScreen):myScreen.move(4,0).set('#').它等价于:myScreen.move(4,0);myScreen.set('#').很明显,这个操作必须返回一个引用,指向执行操作的那个对象.即:
public:
Screen& move(string::size_type r,string::size_type c);
Screen& set(char);
Screen& set(string::size_type,string::size_type,char);
这样,函数可以这样写:
Screen& Screen::set(char c)
{
contens[cursor]=c;
return *this;
}
这样看起来似乎很完美,然而每个事物可能都不象它们表面看起来那么简单,比如说这次.如果我们再增加一个display函数.它是const型的.也就是说:display() const.*this本身是一个指向类类型的const指针.这就有问题了.因为这样以来,它返回的类型就变成了一个指向const类型的指针,而这个指针本身也是const类型的.也就是说,既不能改变指针的值,也不能改变指针指向的对象的值.下面的用法是错误的:
myScreen.display().set('#');
因为函数会返回一个本身是const的指向const的指针,不能用set来改变它所指向的值.
解决方法是写一个重载函数,分为const和非const两种情况.如下:
{
public:
Screen& display(std::ostream &os)
{
do_display(os);
return *this;
}
Screen& display(std::ostream &os) const
{
do_display(os);
return *this;
}
private:
void do_display(std::ostream &os) const
{
oss<<contents;
}
};
最后说一下友元,其实这个很简单,要说的是,重载函数必须在友元声明中全部声明.另外还有const static类型的成员,我觉得这个太过于技巧化,不适于初学者,就没有看.
上面的都比较简单,这说明下面的可能就不会再简单了.事实上我认为下面的这些非常难,看了三遍才算是有些眉目了.因为它里面包含了指针,老实说,我感觉,在程序设计中,不管什么地方,只要遇到了指针,就准没好事儿.因为指针的隐藏的危险性,Andrew Koenig,Barbara Moo夫妇在给想成为更好的C++程序员的最重要的三条建议的第一条中就说:避免使用指针.我是这么理解这个建议的:在你完全理解了指针之后,避免使用指针.否则的话,更好的C++程序员就是在学习C++的时候不学习指针,这样有些搞笑吧.
言归正传.