C++作为C的超集,除了支持C的所有功能之外,还有一点特殊的要求,这是作为C++的宣传口号,但实际上,学习C++,不能从C开始,因为两者是有非常多的区别,从面向对象与面向工程的区别来说,这根本就是两个不同的语言,从编译器要求来说,也是有非常大的区别,使用不同的编译器和不同的类库。那么学习C++需不需要C基础呢?当然需要。有C的基础再加上C++的发挥,对程序的理解非常好。C++首先是以一个面向对象的姿态出现,那面向对象就是C++的根本,没有对象,C++就等同于C。对象与类,这些概念,相信很多人都理解,抽象与具体。同现实空间的物体的概念比较像。比喻说人有脸、手、五官等,这些属性,同时人还可以说话、干活这些活动,对应取类中就是属性与函数。从现实空间中我们也可以很容易理解,一个对象的一些属性与功能,从另一个对象的角度来看,它是分可见还是非可见的。从它的父子关系来看也是有可见、非可见及友化可见的。这些面向对象的一些基本概念这里就不多说。那么我们首先从程序的角度来分析类与对象的关系。

【1】类与对象的定义 C++语言作为一种高级语言,它是用来帮助人类进行改造世界的。人类想要改造世界肯定以人类比较容易理解的思维去进行工作。而人类观察的角度来看世间万物,肯定是一个个体,后来人类发展了分类学,将每种个体进行归类,归纳出一些共同的特征和习性。最简单的如家里的狗,狗每家都有,大小颜色都不同,叫声也不一样,但是我们一见,都叫他们狗。这是为什么呢?因为它们具有共同属性和共同习性。C++语言写好的程序作为计算机的控制大脑,其行为与思考肯定需要与人类相符合。因此,人类利用程序来改造世界,首先需要描述这个世界。描述世界的最好的方法就是分类学,将世界事物分门别类进行归纳分析,一方面有利于化繁入简,另一方面也有利于突出重点。在C++中将个体称之为对象,将类别称之为类。属性通过变量声明,习性(行为)通过函数声明。如学生 {学号;姓名;性别;成绩;考试()} ,学生的对象有张三、李四等

image

【2】类的访问控制 前面讲到一个对象具体很多属性与习性,这些属性与习性相对于这个个体之外,有些是可见,有些是不可见。当然你不要说,我要去解剖它,就看得一清二楚了。我们使用C++去描述世界,不是去解剖世界,是去构造一个虚拟的世界。在这个世界中有按照类定义好的生成的不同对象相互交互,因此它们是不具体人类特性去解剖的。在C++中,我们在类中简单的按照可见不可见分为private,public和protect后面还有一种friendly。

【3】对象的初始化 前面说过类与对象的定义,很显然,类是一种分类学,对象才是一个一个体。因此换句话业说,类如同变量类型,而对象就是变量。因此对象显然需要实例化。对象如何实例化,是一门很复杂的学问。那么对象如何在实例化的过程中先初始化。就像人类由细胞孕育出生命一样。是需要初始化,这初始化有外界给予,如母亲的胎盘,也有默认自己就可以创造的。对象初始化在C++中是有规定的。就是在类中声明与类名同名的函数,但没有返回值的。称之为构造函数。构造函数可以不声明,缺省编译器会提供一个。其访问权限也可是私有,借用其它静态函数创建。

【4】对象的释放 对象创建之后,对象如同世界事物一样,是有生命周期的。否则你创建出无数对象,都不释放。而每种对象都要占用内存空间的,计算机的内存空间如同地球上的资源一样都是有限的。如同生命有死亡有新生,C++中的对象也一样需要释放。这种释放同人类一样,一种当它在这个计算机虚拟世界中不再有存在的价值时,如同人老了一样,它会自动释放,它使用析构函数。另一种是由计算机C++中内的主Delete,将这个对象杀掉,就像人类突然被杀一样。这两种对象释放,它都是经过析构函数过程。析构函数系统会默认提供一个,也可以由类中单独实现,这就好比人有自然死,也有分尸死。析构函数是无参。

前面我们是从程序的角度来分析了对象与类之间的关系,接下来我们从计算机内存的角度来看对象的创建释放,那有人说我们为什么不看类的他那与释放呢?我们前面说了对象创建之后才会占有实际内存。而类的声明只是在编译器生成的符号表分配一个地址内存。从内存分配的角度来说,只有两种方式:

【1】静态分配 如CStudent cstudent1;或者CStudent cstudent[10]或者 CStudent *p; p = &cstudent1,这些对象在声明时就进行初始化,并分配固定内存大小,至于说这些对象放置于动态内存区还是静态内存区,取决于类型前面的修饰符,如果是static或全局通常是静态内存区,其它都在动态内存区。

【2】动态分配 动态分配的内存都在堆上,这是由程序员唯一可以控制的内存区域,因为这块内存区别完全由程序员来控制,操作系统 或者编译器是不知道如何管理这片内存的,因此一定要确保你申请了一片空间,然后在不使用时需要交回给操作系统。如int *p = new int(10); delete p; int *p = new int[10]; delete []p;

从内存存储位置来说,我们也分为如下几种,这些存储位置和C是相同的。

【1】常量区,定义的一些常量字符串或者系统初始化的常量字符串存储在此,这里有一个不同就是C++将使用const定义的常量字符串放在符号表中。

【2】全局区(静态区)全局变量和静态变量放置在此。不论初始化与否,僵尸区的亦是都是有初值的。

【3】栈区(stack),编译器自动分配,存放函数参数值、局部变量、局部对象变量等。

【4】堆 区(heap),程序员自己控制,将想要的变量(普通类型变量或者对象变量等)对应的内存空间也就是rvalue分配在这块区域,需要自己释放

【5】代码区,前面四种可以统称为数值区,这一块是代码区,也就是汇编中用来保存机器指令的存储空间。

//main.cpp  
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{     
     int b; 栈
     char s[] = "abc"; 栈  
     char *p2; 栈  
     char *p3 = "123456"; 123456\0在常量区,p3在栈上。  
     static int c =0; 全局(静态)初始化区  
     p1 = (char *)malloc(10);  
     p2 = (char *)malloc(20);  
     分配得来得10和20字节的区域就在堆区。  
     strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。  

前面对象创建之后,实际上表示的也是一个对象变量,因此必然从编译器的角度,它会有一个符号表用来存储这些变量名称和左地址(lvalue)。C++在C的基础上扩展出一个新的概念,就是对符号表中的变量名称声明别名,为什么这样做呢?别名有什么好处呢?别名跟变量一样吗?别名与指针一样吗?C++中使用引用来声明别名。声明过程只是在符号表中新建了一个别名,这个别名的左地址与它关联的变量是相同的。从这一点就可以看出指针与引用的不同,指针是一个变量名,它除了有左值之外还有右值。别名的左值相当于指针的右值。因为与关联变量具有相同的左值,也就是对应相同的内存空间,因此,它他们的使用是完全一样的。int m = 5; int &n = m; n=6;这时m也就6。引用因为是在符号表中,因此引用一声明就必须初始化,而且还不能改,最重要的是它不在内存中,没有地址,不可取值,也不可建立指向引用的指针和建立引用的引用。了解了引用的一些特点,我们来看下面的函数:

void Func(int &x)

{

x= x+10;

}

int n=0;

func(n);

cout<<”n=”<<n<<endl;

从上面函数来说,n明显作用域不在Func函数中,但通过引用传入Func函数中去后,n作为实参,x是一个引用,同n指向同一块内存空间,神奇的实现了指针的功效。引用虽然很有用处,在很多地方起到类似指针的作用。这是因为它与指针都具体共同特点,就是存储关联对象的内存地址。只是一个作为左地址存储为引用,另一个作为右地址存储为指针。因为引用并无右值,所有引用使用时也是需要注意如下几点:

【1】引用不能返回局部变量的引用,这一点与指针一样。

int &func()

{

  int a = 10;

  int &b = a; //a是局部变量

  return b;

}

【2】引用不能关联数组的引用

  char name[5] = “Test”;

   char &p = name ;// name 不能作为符号表关联,那么name[5]行吗?肯定不行,后面是值了,不是变量了。

【3】引用不建立引用的引用,也不能通过让一个引用指向一个返回的引用。

    int a =10;

     int &b = a;

     int &c =b; //不能引用指向引用。引用没有右 值,所有引用的引用是不合法的。

      int & f(int &a)

         {

         return a;

         }

     int n =100;

      int &m = f(n); //这也是引用指向了引用,需要修改成f(n) =200;

【4】引用不能关联到直接new出来的空间,没有在符号表中声明的变量

    int &r = *(new int(99));

     cout<<r<<endl;

     delete r ;//因为r没有对应的符号表中的变量,无法删除。