1.C++数据结构–面向对象程序设计一
温故而知新,继续数据结构的学习。
1.C++面向对象程序设计
在学习C++之前,我一直以为C++和java是一样的。但是从现在看来,必须改变这种想法,它就是c语言。
1.1 抽象数据类型
从我的角度来看,abstract data type就可以看成伪代码,更像是一种框架。
1.2封装
对于这个来说,其实也没有那么神奇。这个是从类中诞生的。首先创建一个类,在类中需要实现每个功能模块,而模块是由函数形成的,所以可以这样进行理解。封装就是类中的函数。不过,当另外一个类需要调用这个方法的时候,就需要通过类这个接口来去调用。
1.3继承
对于OO 的第二个特性而言,继承在泛C语言家族中都是一样,子类继承于父类,子类可以调用父类中可以调用的方法。在这里要说一点的是C++中的继承分为3种是很扯淡的一件事情,这一点确实没有c#要好,它把继承和接口统一用class a:b来表示。因为父类中权限关键字已经足够了,根本就没有必要再子类调用时改变特性,在这本说中的解释是,“派生类还能够决定或者修改哪些公有和受保护的成员函数和数据成员”,但这是多此一举的事情。
如果子类的基类是爷爷类以上,但是就是需要调用它的话,这时就需要跨级调用同名方法,如BaseClass::f()。::的作用就是指定作用域。值得注意的是,c++中的继承是多继承,但是java中的继承是单继承。其实在继承的方面,我其实是比较支持java的,因为如果两个父类中有一个相同的方法,子类去继承它的,这个方法显然就冗余了。
对于继承的特性,还有就是强制实现和虚实现,就是关键字abstract和vritual,class a:public virtual baseclass{};这个类包含修饰符virtual,这意味着子类仅仅包含baseclass中成员方法的一个副本,子类的子类想要调用子类的方法是会报错了,因为子类中就是一个副本,根本就没有生成。
1.4指针
通常我们会声明一个变量,int a=10;在内存区域中会开辟一个区域,里面的内容就是10;而指针就是存放10位置,也可以这么说指针里面存放的是地址而不是内容。但是你如果将地址也当做一个值得话,也是没有问题的,那就可以说指针变量了。
int *p;*p=20;这里的星号(*)是一个间接寻址运算符,该运算符强制系统首先找到p的内容,然后根据从p中得到地址来访问p指向的内存地址来访问p指向的内存地址,之后将20赋给这个位置。书上说,指针也可以指向匿名位置,这些匿名位置只能通过地址访问,而不能像变量一样通过名字访问。这些位置必须由内存管理器隔离。
P=new int;我们可以用java的语法来看,new在java中的作用就是用来实例化对象的。所以就可以这样进行理解,首先我们定义一个int 类型的指针,但是这个指针可以把它看成一个类,然后就进行实例化,这类类指针的作用就是存放地址。值得注意的是,这样的写法是动态分配。
如果想要删除的话,delete p;在这之前,首先要搞懂的是删除什么?删除这个指针的类型,就是int,但是p所包含的地址还是存在的。换句话来说,就是指针已经没有类型了,但是地址还是存在的。如果删除指针而不将指针变量清除的话这样是很危险的。换句人话来说,你指针永远都是有两个属性的,一个就是地址一个就是类型,二者必须全部去掉。类型直接删除,地址归0。Delete p; p=0;
在书中又说到了一个问题就是内存泄露。内存泄露的本质就是值与地址不一一对应。
P=new int;p=new int;连续两次给p进行动态地址分配,第一个就会出现泄露。
1.4.1指针与数组
在编程语言中的数组在一开始就需要知道它的大小,可以这么说在面对未知的需求的时候数组的大小是难以预测的。但是这句话也是有点不对的,这一些游戏开发中,数组的大小往往又是固定的。现在我们就暂且认为它是不可预测的吧。
Int a
注意,a+1是数组a中下一个元素的位置,所以a+ 1等同于&a[1]。因此,如果a等于1020,那么a+1不是1021,而是1022,因为指针的算术运算依赖于它的指向的实体的类型。Int为2一个字节。
值得注意的是,如果int *p指向的是一个数组,当完成任务后需要删除,可以delete[] p;注意语句中使用了空的方括号,这个括号表明p指向一个数组,此外,delete只应该用于使用new赋值的指针。在这里可以进行联想一下,如果指针指向的是一个类的话,那个删除会是delete class p; 吗?
1,4,2指针与复制构造函数
看如下的代码:
struct Node{
char* name;
int age;
Node(char *n=” “,int a=0){
name=strdup(n);
age=a;
}
};
以前在学习C语言的时候,怎么看struct怎么觉得变扭,但是从class的角度来看的话,就会变得豁然开朗。里面的Node不就是构造函数吗?
接下来看如下的代码:
Node node1(“Roger”,20),node2=node1;
strcpy(node2.name,”Wendy”);
node2.age=30;
cout<<node1.name<<’’<<node1.age<<’’<<node2.name<<’’<<node2.age;
结果为:
Wendy 20 Wendy 20
本来的目的是名字和年龄都不同,但是名字却是一样的。于是我们对其进行分析,就发现node2没有进行单独的实例化,而是直接复制node1,这就导致一个问题,系统在创建一个类的时候,通常是由构造函数开始的,就算自己没有构造函数,系统也会进行指定,而node2是直接复制node1的,于是就直接复制它的构造函数。又因为name是一个指针,它们各自的name于是就指向了同一个指针地址,只要改变它们其中的任意一个name,都会发生改变。就像图中一样:
如何改变这样的情况呢?我们在学习java中,就提到一种很重要的特性—重载。就是名字相同,但是里面的参数不一样,系统可以自动适配。那可以找到构造函数也是可以进行重载的。
于是就可以这样写:
struct Node{
char* name;
int age;
Node(char *n=” “,int a=0){
name=strdup(n);
age=a;
}
Node(const Node &n){
name=strdup(n.name);
age=n.age;
}
};
对于Node(const Node &n),为什么要写const?这个是我自己的理解。在前面已经说过了当我们直接复制一个类的时候,由于本身是没有构造函数的,需要将被复制的类的构造函数先复制下来,再进行构造,只要需要复制就会一直出现这种情况,因为这里的复制是一种值传递,就是完全复制。如何解决这种问题呢?
就是讲形参改为引用形参。
在这里我似乎有点疑惑,我们重新来看这段代码:
struct Node{
char* name;
int age;
Node(char *n=” “,int a=0){
name=strdup(n);
age=a;
}
Node(const Node &n){
name=strdup(n.name);
age=n.age;
}
};
如何只有上面的Node构造函数,当node2(node1);原来如此,我一直理解错了这句话的意思,node2(node1);等于node2=node1;这其实就是一种值传递,node的构造函数会再执行一遍,由于其中的n是一个指针,就会再复制多少遍,每一个类中的n都是指向同一个地址。我们可以通过以下的代码来验证:
int main(){
Node node1(“Roger”,20),node2=node1;
Node node3=node2;
strcpy(node2.name,”Wendy”);
strcpy(node3.name,”Wendy1″);
node2.age=30;
node3.age=40;
cout<<node1.name<<‘ ‘<<node1.age<<‘ ‘<<node2.name<<‘ ‘<<node2.age<<‘ ‘<<node3.name<<‘ ‘<<node3.age;
system(“pause”);
}
这就是指针的特性了,如何你创建多少个变量,只要指向的地址是相同的,只要有一个指针变量改变地址的内容,其他读取的内容全部都发生改变。
现在的一切都豁然开朗了,前面的直接复制是一种值传递,然后我们采用第二种方式,node2(node1)。如果采用第二种方式,node1真的就成为变量传入node2的构造函数中,至于为什么采用&的方式,就是因为这里的node1就称为一个实参在传递了,当然实参在传递的过程中,node1一定要注意的是不能改变,这就是const的作用了,就是为了防止实参的改变。
说了这么多罗里吧嗦的话,复制到底是采用值传递的方式去做,还是采用引用传递的方式去做?类中的成员到底希望是采用值传递还是引用传递?最后说明一样,结构体我就当做类来看的。
1.4.3指针与析构函数
在java中其实也有析构函数,但是一般都用不到。一般java中用GC。在c++中,当销毁对象、程序从定义对象的块中退出或调用delete时,析构函数都会自动调用。
~Node(){
If(name !=0){
Free(name);
}
}
1.4.4指针和引用变量
指针是 int *p=”fdf”;
引用是 int& r=1; 或者int *const r=&n;(固定的指针就是引用了,哈哈~~)。
指针可以说动就动,而引用则不行,引用变量必须在声明中初始化为一个特定变量的引用,而且这个引用不能改变,这就意味着引用变量不能为空。感觉这个有点像const。
1.4.5函数指针
说真的,这个函数指针我是一直都搞不懂,现在开始认真的学习一下。书中的第一句话还是重要的。变量的一个属性就是其地址指明了它在计算机内存中的位置。这同样适用于函数:函数的一个属性就是其地址指明了函数体在内存中的位置。
首先来看函数指针的定义:函数类型 (*指针变量名)(形参列表)。
例如:int (*f)(int x);
又如 int func(int x);//声明一个函数
int (*f)(int x);//声明一个函数指针
f=func;//将func函数的首地址赋值给指针f
对于函数指针而言,首先我们要肯定的是它一定是一个指针,而这个指针的类型是函数类型的,那么它就包括函数的功能,就是参数。而(*f)的作用,就是声明它是一个函数指针。
此外还有一个就是指针函数,顾名思义就是返回值是指针的函数。
格式为:类型标识符 *函数名(参数表) int *f(x,y);
1.5多态性
作为三大特性的最后一个特性—多态性。多态性指的是获得多种形态的能力,在oop中,多态性指的是用同样的函数名称表示多个函数。其实它的实现功能比较简单,在基类中声明多个方法,不过这些方法,需要在前面加上特性,用virtual和abstract,然后子类根据这个是否修改方法。
最后说一个很好玩的事情,在C语言中的struct的格式为:
structBooks
{ chartitle[50];
charauthor[50];
charsubject[100];
intbook_id;
}book;
而感到神奇的事情就是class的定义也可以用这种方式:
class C{
int n;
friend int f();
}obj;这个定义就和struct一样了。看来说C++就是多个类的C是一点没有错啊。
1.C++数据结构–面向对象程序设计一
原文地址:https://www.cnblogs.com/jake-caiee/p/13474188.html