c++类
话不多说,看一段代码:
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
该段代码中,使用class创建一个Box类,可以初始化一个对象:
Box Box1
非常简单的初始化了一个Box1对象。
这个时候,Box1就可以用**(.)成员访问运算符访问public里面的成员**,对于private和protect的成员,不能用(.)进行访问。注意一点,成员默认为private的。
具体如何访问,后面再记!
类成员函数
首先,理解什么是类成员函数,既然有“成员”两个字,那么肯定是属于类的一个成员,就跟定义类中其他成员一样。
类成员函数可以操作类的对象,也可以访问对象的成员。
举个例子:
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void);// 返回体积
};
在类中声明了一个getVolume函数,既然是在类里面,那肯定是成员函数。下面来创建一个对象:
Box Box1
Box1就可以使用这个成员函数,怎么使用呢?用成员访问运算符
(跟上面说的 public 成员一样的)。
Box1.getVolume()
就可以使用这个成员函数了。
这个成员函数只有声明,没有定义函数体。有两种方法:
第一种:在类里面定义
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void)
{
return length * breadth * height;
}
};
直接写函数体内容就完事,注意:上面说过,成员函数是可以访问对象的成员的哦!
第二种:在类外面定义
double Box::getVolume(void)
{
return length * breadth * height;
}
看到这个形式了吧,一定要写类名!然后用范围解析运算符 ::
进行定义!
然后发现长宽高都没有定义啊,return了个寂寞。
其实可以写多个类成员函数,进行初始化赋值。
后面有个简便初始化赋值的方法——构造函数。(后面再说)
类访问修饰符
类访问修饰符分为三个:public private protected
先说第一个 public :
这个比较熟悉,可以被外部访问,那么什么算是外部呢?其实就是可以被对象直接使用,比如:
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
在main函数中声明一个对象 Box Box1 。
那么要对成员进行初始化,两个方法:要么写一个类成员函数,对其进行赋值,还有一种,重点:直接进行赋值。
就是 **Box1.length = 2.0 …**直接进行访问,因为length是开放的。
然后就是 private :
只有类和友元函数能访问私有成员。
就是说不能被对象在外部直接访问,但是可以用公有的成员函数进行访问赋值。(为什么要强调公有呢?因为公有才可以调用,如果设置私有成员函数,那还怎么调用它?)
然后就是protected :
这个和私有成员差不多,有一点:其派生类可以访问!
举个栗子(上代码):
class Box
{
protected:
double width;
};
class SmallBox:Box // SmallBox 是派生类
{
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};
定义一个Box 类,派生了一个SmallBox类
Box有个protected成员,SmallBox有两个成员函数
// 子类的成员函数
double SmallBox::getSmallWidth(``void``)
{
return width ;
}
void SmallBox::setSmallWidth( ``double` `wid )
{
width = wid;
}
看看main函数:
int main( )
{
SmallBox box;
// 使用成员函数设置宽度
box.setSmallWidth(5.0);
cout << "Width of box : "<< box.getSmallWidth() << endl;
return 0;
}
声明一个SmallBox类的对象box,box能访问父类里的width,进行赋值,这就是一个很好的例子。想一想,能不能直接访问呢?
就是 box.width = 5.0 ,动手试一试。
运行结果是不行的,为什么不行,不是可以访问吗?
忽略了一点,protected是跟private相似,回顾一下private的访问方法,只能被类和成员函数访问,既然SmallBox继承了Box,那么Box就继承了父类的属性,SmallBox的成员函数就能访问private成员,即访问父类的类似于private的protected成员!
构造函数和析构函数
构造函数属于类的一种特殊的成员函数,与类同名,在创建新对象的时候会调用,没有返回值,可以有参数。带参数的构造函数一般用于类成员的初始化。
对于析构函数,一般在删除对象的时候会执行,析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
如果程序里没有构造函数和析构函数,编译器在编译的时候会自动生成构造函数和析构函数,只是函数内没有任何操作。
拷贝构造函数
看下方一段代码
#include<iostream>
using namespace std;
class Line{
public:
int Getlentgh(void);
Line(int len);
Line(const Line& obj);
~Line();
private:
int* ptr;
};
Line::Line(int len) {
cout << "调用构造函数为指针分配内存!" << endl;
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj) {
cout << "调用拷贝构造函数为指针分配内存!" << endl;;
ptr = new int;
*ptr = *obj.ptr + 1;
}
Line::~Line() {
cout << "释放内存" << *ptr <<endl;
delete ptr;
}
int Line::Getlentgh(void) {
return *ptr;
}
void display(Line obj) {
cout << obj.Getlentgh() << endl;
}
int main()
{
Line line1(10);
Line line2 = line1; // 这里会调用拷贝构造函数
display(line1);
display(line2);
return 0;
}
下面是输出:
调用构造函数为指针分配内存!
调用拷贝构造函数为指针分配内存!
调用拷贝构造函数为指针分配内存!
11
释放内存11
调用拷贝构造函数为指针分配内存!
12
释放内存12
释放内存11
释放内存10
说说我的分析:
在main函数内,创建了一个line1对象,那么,肯定会先调用构造函数,因为构造函数会在创建新对象的时候调用,所以输出的第一行是line1的构造函数执行了。
现在看后面的拷贝构造函数调用,是怎么回事呢?先来看看什么是拷贝构造。
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于(被调用于):
① 通过使用另一个同类型的对象来初始化新创建的对象。
② 复制对象把它作为参数传递给函数。
③ 复制对象,并从函数返回这个对象。
拷贝构造函数的主体为:
classname (const classname &obj) {
// 构造函数的主体
}
对于拷贝构造函数的用途,很显然,该段示例中用了两种——①和②,且听我慢慢道来。
既然说可以通过一个同类型对象初始化新创建的对象,那么在代码中,利用line1 给 line2 进行了初始化,那么肯定会调用一次拷贝构造函数,所以输出的第二行就是这么来的。
再看输出的第三行的拷贝构造函数,怎么来的呢?
因为将一个对象当成形参传递给函数时,也会调用拷贝构造函数,所以在main函数中,**display(line1)**就会调用拷贝构造函数,就是这么来的,传递给函数后,由于调用了拷贝构造函数,它的值加一,所以会打印出11。
可能又会问,不是调用了两次吗,为什么不是12呢?
因为第一次调用,是给line2对象进行初始化,所以line2的初始是10,然后执行的是line2的拷贝构造函数,line2的变成11,这时候line1的还是10,当第二次执行拷贝构造函数的时候,就是把line1作为参数传进display,所以第二次执行的是line1的拷贝构造函数。
然后display()执行结束,把形参(局部)line1给释放掉了,所以会调用局部的line1的析构函数。
然后又是一个执行拷贝构造函数,不用多说,肯定是line2作为参数传进display(),给调用了一次,还记得初始化调用了一次吗,那时候为11,这时候又调用,那就为12了,然后打印12出来,释放参数。
重点来了,为什么最后两个释放呢?为什么一个11,一个10?
先知道,肯定是return 0,结束了,然后执行了line1和line2的析构函数,这个没毛病吧?那个值是怎么回事呢?
先给个答案,11的那个是line2 的,10的那个是line1 的。为什么呢?
因为在初始化line2的时候,调用了一次拷贝构造函数,那时候line2的变成了11,注意,没有被释放!只释放了变成12的那次!但是line1的是通过作为参数的方式传进函数然后执行拷贝构造函数的,会被释放掉,所以还是初始值10。
SO,大体就是这样,所以要牢记调用拷贝构造函数的条件!注意第三点:对象作为函数返回值也会调用一次!
拷贝也有深拷贝和浅拷贝,后面再说!