摘要:1.why?
2.面向对象程序设计
3.类的成员:成员变量,成员函数,可访问范围
4.使用类的成员变量和成员函数的方法
5.类成员地可访问范围
6.成员函数也可重载,也可有缺省值
7.构造函数
8.复制(拷贝)构造函数
9.类型转换构造函数
10.析构函数
1.why?
c语言中:程序=数据结构+算法
随着程序规模增加,程序逐渐难以理解,很难一下子看出来:
- 某个数据结构到底有哪些函数可以对它进行操作
- 某个函数到底是用来操作哪些数据结构的
- 任何两个函数之间存在怎样的调用关系
结构化程序设计不足:没有封装和隐藏的概念,重用时抽取代码困难
2.面向对象程序设计
面向对象程序=类+类+类+……+类
设计程序的过程为设计类的过程
抽象:将特点和行为归纳出来
封装:将数据结构和操作该数据结构的函数捆绑在一起,形成一个类,使其呈现出显而易见的紧密关系
隐藏:设置私有成员的机制
基本特点:抽象,封装,继承,多态
3.类的成员:成员变量,成员函数
类:带函数的结构
对象:类定义的变量,类的实例
类的名字:用户自定义的类型的名字
对象的内存分配:和结构变量一样,等于所有成员变量的大小之和
对象之间可以用“=”进行赋值,但不能进行比较(除非那些运算符被重载)
类的成员函数和类的定义可以分开写,仅在定义中声明成员函数,内容放到类外写
例:int CRectangle::Area(){
return w*h;
}
“ CRectangle::”说明后面的函数是这个类的成员函数,不是普通函数
类成员的可访问范围
private:私有成员,只能在成员函数内访问
public:公有成员,可以在任何地方访问
protected:保护成员
PS:以上三种关键字出现次数和先后顺序都没有限制
PS:如果某个成员前无上述关键字,则缺省地被认为是私有成员
4.使用类的成员变量和成员函数的方法
- 对象名.成员名
- 指针->成员名
- 引用名.成员名
5.类成员地可访问范围
- 类成员函数内部
当前对象的全部属性、函数
同类其它对象的全部属性、函数
PS:成员函数以外,只能访问该类对象的公有成员
6.成员函数也可重载,也可有缺省值
注意避免重载时的二义性
例:void valuex(int val=0){}
int valuex(){}
A.valuex();//报错,有二义性计算机不知道调用哪一个函数
7.构造函数
名字与类名相同,可以有参数,不能有返回值(void也不行)
作用:对对象进行初始化(不是给对象分配空间)
如果定义类时没写构造函数,则编译器生成一个默认的无参数构造函数(不做任何操作)
对象生成时构造函数自动被调用,一旦生成,再也不能在其上执行构造函数
一个类可以有多个构造函数
例:三个构造函数
CSample (){}//(1)
CSample (int n){}//(2)
CSample (int n,int m){}//(3)
int main(){
CSample a(1);//调用(2)
CSample b[2]= {4,5};//数组中两个元素调用了两次(2)
CSample c[2]= {3};//数组中第一个元素调用(2),第二个元素调用(1)
CSample d[3]={1,CSample(1,2)};//数组中三个元素分别调用(2),(3),(1)
重要:CSample * e[3]={new CSample(4),new CSample(1,2)};//定义指针数组并不会自动调用构造函数,需要用new,所以这个数组中前两个元素分别指向由(2)初始化的new出来的对象和由(3)初始化的new出来的对象,最后一个指针元素没有初始化
8.复制(拷贝)构造函数
X::X(X&) 或 X::X(const X&) //多用后者,能以常量对象作为参数
只有一个参数,即对同类对象的引用
如果没有定义复制构造函数,编译器会生成默认复制构造函数
复制构造函数起作用的三种情况
-
用一个对象去初始化同类的另一个对象时
Complex c2(c1);
Complex c2=c1; //首次出现是初始化,不是赋值 -
如果某函数有一个参数是类A的对象,那么该函数被调 用时,类A的复制构造函数将被调用
void Func(A a1){}
int main(){
A a2;
Func(a2);//自动调用复制构造函数,a1是a2的复制品
return 0;
调用这样的函数时,生成形参会引发复制构造函数的调用,开销大,可以考虑使用引用类型作为参数。
如果想确保实参的值在函数中不应被改变,可加上const关键字变成常量引用。
这样若函数中有改变实参的值的语句,编译器会报错。 -
如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用
PS:对象间的赋值并不导致**复制(拷贝)**构造函数被调用
问题:为什么要自己写复制构造函数?用编译器默认的不也能完成复制任务吗?
9.类型转换构造函数
目的:实现类型的自动转换
定义:【只有一个参数,且不是复制构造函数】的构造函数
需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)【需要创建和消亡的】
例:Complex(int i){}//是转换构造函数
Complex(double r,double i) {} //不是
Complex c1(7,8);//不调用转换构造函数
Complex c2=12;//调用
c1=9;//9被自动转换成一个临时Complex对象,再赋值给c1
10.析构函数
名字与类名相同,在前面加‘~’,没有参数和返回值,一个类最多只能有一个析构函数
对象消亡时自动被调用
可以定义析构函数在对象消亡前做善后工作(比如释放分配的空间)
如果定义类时没写析构函数,则编译器生成缺省析构函数,它什么也不做
对象数组生命期结束时,对象数组的每个元素的析构函数都会被调用
例:
class CMyclass{……
……
};
CMyclass obj;
CMyclass fun(CMyclass sobj){
return sobj;
}
int main(){
obj=fun(obj);//fun函数结束时形参sobj消亡调用一次析构函数;复制函数生成临时对象返回后,临时对象消亡调用一次析构函数
return 0;
}//程序结束,全局对象obj消亡调用一次析构函数
总共调用三次析构函数
{static Demo d1(1);
Demo d2(2);}
//大括号结束时只调用一次析构函数,因为静态局部变量在整个函数结束时才消亡
一般来说先构造的先析构
delete时调用析构函数
new出来的对象,必须用delete释放,否则即使程序结束也不会消亡不会调用析构函数
PS:Dev编译器中,系统会自动优化,函数返回一个对象时不调用复制函数生成临时对象,直接返回。
Visual Studio正常