C++学习

①.指针与引用

  1.C++提供两个指针运算符,分别是“*”和“&”。第一个符号有两种含义:1.指针定义符号,用来指定某一个变量是指针。2.间接引用符,取出指针变量所指向的内容。第二个符号也有两种含义:1.引用定义符号,用来定义一个变量的别名。2.取地址操作,取出变量的地址。

  2.在参数传递的时候,函数的形参是对实参的拷贝,当数据量很大的时候,拷贝显然是很耗费资源的,这时候如果用指针的话仅需传递地址即可,避免了实际数据的大量拷贝。

  3.什么时候用指针或是引用呢?当需要定义一个地址时,它不指向内存任何区域,那么该用指针,或者需要指向的地方变来变去,也应该使用指针。当你一旦指向了某块内存区域并且以后只指向它时,需要用引用。

  4.有指向数据类型的指针,也有指向函数的指针。指向函数的指针存储的是函数代码块的首地址,作用和指向数据类型的指针很类似。

  5.C++参数传递分三种:值传递、地址传递、引用传递。值传递的特点就是在被调用函数中建立所主调函数所传递值的拷贝,你对拷贝进行修改当然不会影响到主调函数变量的值~~。地址传递本质上也是值传递,传进来一个地址的拷贝,地址本身就是一个数值而已,你在被调函数对地址拷贝进行修改,当然不会影响到实参变量的地址的,当然你想修改实参变量的值的话,还是可以做到的,比如传进来一个指针p,用*p=10这种方式就可以对p指向的实参变量进行修改了。引用传递也是传递进来一个地址,但是与地址传递不同的是,被调函数对形参的任何操作都是默认通过间接寻址来对实参进行操作。引用传递比指针传递更简单和直观。

  6.指针数组。与数组的区别仅仅是,它的每个元素存储的是某个数组的首地址。  

②类与对象

  1.访问控制。public,内部外部都可以访问。protected,本类或是继承类可以访问。private,只有内部可以访问。

  2.拷贝构造函数。普通的变量可以赋值给另一个变量,同理一个实例也可以赋值给另一个实例。分深拷贝和浅拷贝,深拷贝其实就是自定义拷贝,浅拷贝是默认的。

  3.组合类。也就是大类包含小类,比如班级类里面包含学生类。生成对象时,先调用小类的构造函数,再调用大类的构造函数。析构时,反之。

  4.友元机制,包含友元函数和友元类。友元函数:在一个类中定义一个友元函数(但它并不是本类的成员函数哦),那么这个友元函数可以访问类中的私有成员和保护成员了。若类A是类B的友元(需要在B中定义A),那么A中的成员函数可以任意访问类B的成员。

  5.重载。数据类型不同,名字相同。还有一个运算符重载,我们可以对基本类型加减乘除操作,比如int,double等,但是如果想对非基本类型操作,那么就需要对运算符进行重载,比如对“+”操作重载:operator+()。通常返回类型都与它所操作的类的类型一样。

  6.虚继承。为了消除二义性(A、B继承自C,D由继承A和B,当C有一个方法x()时,显然D中就有了两个x()),但是没有二义性不就完了?这个功能比较鸡肋。

  7.多态、联编、多态有两种:编译期多态和运行期多态。什么意思呢?编译期多态就是在编译的时候将标识符和地址进行绑定(也叫联编),运行期多态就是在运行的时候将标识符和地址进行绑定。上面的联编分静态联编和动态联编,静态联编是在编译之前进行,动态联编实在编译之后进行。

  8.虚函数和虚类。虚函数:父类和子类都有函数A()。根据需要访问父类或是子类的A(),所以A要被定义成虚函数,可能是虚无飘渺的意思吧。更极端的例子是虚函数,它没有实现体只能在子类中实现。包含纯虚函数的类是抽象类,它不能被实例化,由它的派生类实现它的纯虚函数。虚函数表:虚函数的作用是实现多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

在这个继承关系中,子类没有重载任何父类的函数。实例Derive d的虚函数表如下:1)虚函数按照其声明顺序放于表中。2)父类的虚函数在子类的虚函数前面。

在这个类的设计中,我只覆盖了父类的一个函数:f()。1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。2)没有被覆盖的函数依旧。

我们在子类中覆盖了父类的f()函数

  9.虚析构函数。虚析构函数是指将基类的析构函数加上virtual。比如A* p = new B(); delete a; delete a给我们的第一印象是删除a(调用A的析构),但是b就没法删除了(产生内存泄露),所以我们将A的析构加上virtual关键字,在delete a时根据实际指向的对象来删除它,也就是实现了多态(析构函数动态析构)。

 

③模板、命名空间和头文件

  1.模板的概念:所谓模板就是我有一段代码,这段代码可以处理不同类型的问题。

  2.模板函数模板类模板。函数模板:template<class 类型标示符>返回类型 函数名(函数形参表),模板也可以被重载。类模板:template<类型参数表> class 类名{};

  3.命名空间比如:using space std,是将std空间中的所有类、模板和函数等都引入进来。

  4.头文件。保存程序的声明(.h),另一个文件用于保存程序的实现(.cpp)。用#include <*.h> 格式来引用标准库的头文件,用 #include “*.h” 格式来引用非标准库的头文件。建议将成员函数的定义与声明分开,不论该函数体有多么小。另外,只需要按照头文件中的接口声明来调用库功能,不必关心接口是怎么实现的,编译器会从库中提取相应的代码。为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。#pragma once则由编译器提供保证:同一个文件不会被包含多次。

  5.extern关键字。extern说明变量或者函数声明在其他的源文件里,而不用include头文件的方式来引用该函数,在链接时,链接器在各个模块中搜索这个变量或者函数来进行最终链接。

  6.this关键字。类由数据成员和函数成员构成,在生成n个对象时,为n个对象的数据成员分配n块空间,但是成员函数是对所有对象所共享的,也就是只有1份,目的是为了节省空间。那么不同对象在调用这1个成员函数时,怎么识别哪个对象调用它呢?系统默认一个操作是this=&对象,也就是先将对象的地址赋值给this,然后用this对它所指向对象的数据成员进行操作,从这可以看出来,this代替的是未知对象的地址,其实每个成员函数中都藏有this指针。

④函数、STL和异常

  1.C++的STL主要包含:容器(也叫数据结构)、算法、迭代器,用的时候需要将它们的头文件引入进来。

  2.异常有三个步骤1.检查try:将可能出错的代码放在里面。2.抛出throws,当程序出现错误时而本身又不处理可以用throws将其抛出。3.捕捉catch,有异常时处理异常,没有时则不执行,如果这里不处理则抛给调用者。

  3.STL容器里存放对象还是指针?http://hsw625728.blog.163.com/blog/static/3957072820091116115732821/

  4.内联函数inline:函数调用的时候会产生一些压栈、寻址等操作,也就是说会有一定的资源消耗,当函数体很小并且被频繁调用时,这样做效率显然是很低的。用内联函数时,并不产生函数调用,只是把内联函数体直接放到它在主调函数所在位置。但当内联函数体很大时,直接放过来的话会使主调函数体显得冗长,得不偿失。

  5.指针函数函数指针。指针函数的意思是返回指针类型的函数,需注意的是,不能返回局部变量的地址,局部变量在调用函数结束时就销毁了,要返回全局变量或静态变量的地址。函数指针的本质是函数的地址,应用场合是:需要将函数作为参数传入调用函数中,要传入的函数是不固定的,也就是不确定将哪个函数传进来,因此可以用函数指针来代替这个不确定的函数。

  6.malloc函数。它的作用是内存分配,它的参数是你要分配的内存字节数,返回值是一个指针,它指向这块内存的首地址,内存向来是有借有还的,用完之后要用free释放。与new的区别是,new先分配内存,然后再调用构造函数,相当于new=malloc+构造函数调用。如果仅是内存分配的话只需调用malloc即可。

  7.深拷贝浅拷贝。先说下拷贝构造函数,将已经存在的一个对象复制到另一个同类型的对象当中(比如常见的A a2=a1),它的名称必须与类名称一致,函数的形式参数是本类型的一个引用变量,比如X(X& x),且必须是引用,默认的拷贝就是浅拷贝,拷贝的只是名字,它们内部的资源都是在同一块堆上的,析构的时候会出现问题。深拷贝就是我不但拷贝名字,还把内部的资源也拷贝一份不出来。

⑤线程和预处理

  1.一个程序中可以定义多个sections,它又可以定义多个section。sections与处于串行状态,而section之间处于并行状态。如果要使sections之间并行,只需在sections后加上nowait即可。

  2.typedefdefine。typedef是为了让程序变得简洁,给已有数据类型起个新名字。define只是起到文本替换的作用。它们看起来都是起到替换的作用,但是typedef不能扩充,而define可以扩充。

  3.预处理。在程序编译之前需要一些预处理操作,只在开始的时候编译一次,之后不在编译,除非对预编译的文件进行修改。文件包含:#include,在指令处展开被包含的文件,比如"stdafx.h"里面包含一些常用的头文件,我们把它include进来的话可以少写很多头文件。条件编译#if,#ifndef,#ifdef,#endif,#undef,根据条件有的编译,有的不编译控制:偶尔用的有个叫#pragma,指定编译器要做的动作,比如#pragma once避免文件被多次包含,你在一个头文件中用了这个指令的话,那么这个文件可避免多次被包含(包含的话直接被忽略,不打开)。而#if,#ifndef则需要打开再判断。宏替换,前面说过了。 

posted @ 2012-09-13 17:08  Wall^E  阅读(245)  评论(0)    收藏  举报