C++学习:类与对象
参考书籍:《c++程序设计》张军编著
1.面向对象
类 就是描述一类问题的共同属性和行为。(共同的,是吧,用个类,就方便使用。属性,一般就是参数咯,行为,一般就是函数噻),他是抽象的,就像“时钟”,只是一种概念,不占存储空间,在定义类时,不能初始化。
对象呢,就是类 实例化的一种实体,比如我面前这个时钟(参数被赋值,这里多大什么颜色的时钟)
比如,我建立一个类:
class people
{public:
void setAge(int ageInput)
{
age=ageInput;
}
private:
{
int age; //一般把属性,数据藏起来,你外人没事别把他搞坏了。函数你随便调用问题不大
}
}
这就建立了一个叫people类,属性:年龄age;行为:设置年龄;
怎么实例化呢?
className objectName
比如:people Zhang;实例化了一个叫张三的人。
这样我们就可以叫Zhang的人了。
Zhang.setAge(12) //设置Zhang的年龄12
cout<<Zhang.age<<endl 输出Zhang的年龄;
有了这个类people,我们就可以玩无数个对象了噻,这就是类的好处。
来,我们看下书上的一个简单实例:
#include <iostream>
using namespace std;
class Clock //类命名一般首字母大写
{
private:
int hour,minute,second;
public:
void setTime(int h, int m, int s) //注意,对函数的命名,一般采用驼峰形状,第一个单词小写,后面的首字母大写;
void showTime(); //声明函数,然后在后面定义
}
void Clock::showTime()
{
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
void Clock::setTime(int h,int m,int s)
{
hour=h;
minute=m;
second=s;
} //在类外面定义函数的格式 返回值数据类型 类名::函数名;
void main() //主函数
{
Clock c1;//实例化时钟对象c1;
c1.setTime(12,12,12); //调用public的函数
c1.showTime();
}
//运行结果为
//12:12:12
2.内联函数与重载函数
内联函数是为了加快运算速度,重载函数是因为太懒,不想给功能相近的函数取两个名字,所以就让他们使用同一函数名(此之谓重也)
内联函数定义:执行到函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入寄存器中),然后跳回到地址被保存的指令处。
内联函数提供了另一种选择。编译器将使用相应的函数代码替换函数调用。
内联函数的好处:内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。
内联函数的使用:在函数声明前加上关键字inline; 在函数定义前加上关键字inline。
重载函数
两个重载函数必须在下列一个或两个方面有所区别:
1、函数的参数个数不同。
2、函数的参数类型不同或者参数类型顺序不同,
C++的这种编程机制给编程者极大的方便,不需要为功能相似、参数不同的函数选用不同的函数名,也增强了程序的可读性。
3.类成员访问权限
类里默认是私有private,结构体默认是public。
权限还有另外一种类型,叫做:protected。前面我们也有提,
谁想到这样设置数据的呢?
我们回到现实生活中,
一个类,比如时钟 这个类,
他的参数可能有齿轮比,齿轮数,分,时,秒,行为:有时钟设置,时钟显示,齿轮转动的位置读取;
虽然参数很多,但是我们在程序里,关心的没有内部结构那块,我们只关心:分,时,秒,时钟设置,时钟显示,
就以这些参数与函数定义了结构体,并让分,时,秒praivate; 函数 public。这样保护了参数安全,又可以调用。
以我们前面的例子,你不能在main函数里,写c1.minute=1,这样会失败的。
public: 能被类成员函数、子类函数、友元访问,也能被类的对象访问。
private: 只能被类成员函数及友元访问,不能被其他任何访问,本身的类对象也不行。即c1.minute不行。
protected: 只能被类成员函数、子类函数及友元访问,不能被其他任何访问,本身的类对象也不行。
从父类A建立子类B
class B : public A
{ }
子类继承基(父)类里面public和protected中的所有变量和函数。
4.构造函数与析构函数
构造函数的作用?与普通函数的区别?
构造函数主要用于解决class对象的数据成员初始化问题。
如果一个类是完全public的
class Clock
{
public:
int hour, minute, second;
};
Clock c1={12,0,0}
这样就完成了初始化,然而许多class的数据成员是private的,就需要利用构造函数进行初始化;
构造函数的性质:
1. 名字与类名相同;
2.没有返回类型,void也没有,不返回任何值
3. 构造函数由系统自动调用,不用用户调用;
4. 一般来说,构造函数的访问权限为public, 特殊情况下也可以为其他访问权限;
5.构造函数具有成员函数的所有属性,包括 可以重载;可以设置默认形参默认值;
【注意,因为重载与可以设置默认形参值,可能造成二义性】
class Clock
{
public:
Clock();
Clock(int newH, int newM, int newS);
private:
int hour,minute,second;
};
Clock::Clock() //构造函数1
{
hour=minute=second=0;
}
Clock::Clock(int newH, int newM, int newS)//构造函数2
//Clock::Clock(int newH=0, int newM=0, int newS=0)//构造函数2,可以设置默认值
{
hour=newH;
minute=newM;
second=newS;
}
Clock c1; //c1对象调用构造函数1
Clock c2(12,0,0); //c2对象调用构造函数2
//这里利用了其重载特征,所以可以写两个构造函数,根据输入的参数情况,自动识别并初始化
如上,在有默认值的时候,你写Clock c1; 程序就无法识别是哪一个构造函数了,从而造成了二义性。
#include <iostream>
using namespace std;
class Clock
{
public:
Clock();
Clock(int newH, int newM, int newS);
void showTime()
{
cout << hour << ":" << endl;
};
private:
int hour, minute, second;
};
Clock::Clock() //构造函数1
{
hour = minute = second = 0;
}
Clock::Clock(int newH, int newM, int newS)//构造函数2
//Clock::Clock(int newH=0, int newM=0, int newS=0)//构造函数2,可以设置默认值
{
hour = newH;
minute = newM;
second = newS;
}
void main()
{
Clock c1; //c1对象调用构造函数1
Clock c2(12, 0, 0); //c2对象调用构造函数2
//这里利用了其重载特征,所以可以写两个构造函数,根据输入的参数情况,自动识别并初始化
Clock c3(c2);
c3.showTime();
system("pause");
};
当你想复制其中的构造函数初始化,直接用 Clock c3(c2)就可以了。因为其c++内部有定义这样一个复制构造函数的形式的。
class Clock
{
public:
Clock();
Clock(Clock &c)
.....略去,同上;
}
Clock::Clock()
{
hour=minute=second=0;
}
Clock::Clock(Clock &c)
{
hour=c.hour;
minute=c.minute;
second=c.second;
} //意思就是说这个复制构造函数不写也可以
下一步,就来到了和构造函数(Constructor)对应的析构函数(Destructor)
在不使用new创建对象时,对象的内存空间是在栈中的,其作用范围只是在函数内部,函数执行完成后就会调用析构函数,删除该对象。[参考博文:https://www.cnblogs.com/zhengfa-af/p/8109958.html]
而使用new创建对象是创建在堆中的,必须要程序员手动的去管理该对象的内存空间。
构造函数的性质,除了一般成员函数的性质外,还有:
1. 名字必须与类名相同,前面加“~”构成
2. 没有返回类型,包括void,不返回任何值
3. 系统自动调用,不需用户调用;
4. 访问权限为:public
5. 没有形参,不接受任何参数,因此不能重载。
#include <iostream>
using namespace std;
class Clock
{
public:
Clock();
Clock(int newH, int newM, int newS);
~Clock();
void showTime()
{
cout << hour << ":" << endl;
};
private:
int hour, minute, second;
};
Clock::Clock() //构造函数1
{
hour = minute = second = 0;
}
Clock::Clock(int newH, int newM, int newS)//构造函数2
//Clock::Clock(int newH=0, int newM=0, int newS=0)//构造函数2,可以设置默认值
{
hour = newH;
minute = newM;
second = newS;
}
Clock::~Clock()
{
cout << "Destructor is called" << endl;
}
int main()
{
{
Clock c1; //c1对象调用构造函数1
Clock c2(12, 0, 0); //c2对象调用构造函数2
//这里利用了其重载特征,所以可以写两个构造函数,根据输入的参数情况,自动识别并初始化
Clock c3(c2);
c3.showTime();
} //这里为什么要加入大括号呢?因为这样大括号之后,就会删除c1等对象,删除对象之前就会执行析构函数,我们就可以看到析构函数的效果,输出。若没有大括号,sytem("pause")在那,没执行完,是不会执行析构的。
system("pause");
return 0;
};
大括号限制作用域。 在大括号内声明的局部变量其作用域自变量声明开始,到大括号之后终结。
好了,本来c++本来自己也会删除这些对象的,那么析构函数我们拿来有什么用呢?
那么就要利用析构函数的一条重要性质了:类的析构函数在对象被删除之前自动调用。
应用:因此,可以把一些程序的结束清理性工作放在析构函数里(比如,软件结束时,数据库的关闭,服务器的断开等)
关于程序的运行顺序,我们一般认为是从main()开始
可以看出,最先执行的,其实是main函数外的静态区初始化,全局变量。