C++ 学习简单笔记

 

第二篇 基本语言

   空字符是C和C++用来标记字符串结束的符号。
   变量和文字变量都有存储区,并且有相关的类型,区别在于变量是可寻址的,对于每一个变量,都有两个值与

其相关联。一个是数据值,存储在某个内存地址中;一个是它的地址值,即存储数据值的那块内存的地址。

   * 解引用操作符  & 取地址操作符
 
   int *pi = 0;//pi被初始化为“没有指向任何对象”
   int *pi2 = &ival'//pi2被初始化为ival的地址
   pi = pi2;// pi和pi2都指向ival
   pi2 = 0;//现在pi2没有指向任何对象
  需要注意的是指针不能持有非地址值。

   已知一个int类型指针对象pi,int *pi;
  1)当我们写下pi时,这个表示pi当前持有的地址值;当我们写下&pi时,这个表示指针对象pi被存储的位置的地

址;
  2)怎么访问pi指向的对象呢?
     int ival =1024,ival2 = 2048;
     int *pi = &ival;
     *pi = ival2;
     *pi = abs(*pi);
     *pi = *pi + 1;

   判断字符串是否为空的方法:char *str = 0;
                             if(! str || ! *str){return ;}

   string类型能够自动将C风格的字符串转换成string对象,反向的转换不能自动执行。对隐式地将string对象转

换成C风格的字符串,string类型没有提供支持。(要实现这个操作,我们可以调用名为c_str()的操作。)

   数组类型:
   1.维数指定数组中包含的元素的数目;维数必须是常量表达式,即必须在编译时刻计算出它的值。这意味着非

const的变量不能被用来指定数组的维数。如下:
      extern int get_size();
      const int buf_size = 512, max_files = 20;
      int staff_size = 27;
      char input_buffer[buf_size];//Ok,buf_size是const
      char *fileTable[max_files - 3];//OK,max_files是const,常量表达式20 - 3
      double salaries[staff_size];//error,staff_size非const变量
      int test_scores[get_size()];//error,非const表达式

   2.字符数组可以用一个逗号分开的字符文字列表初始化,文字列表用花括号括起来,或者用一个字符串初始化

,但是文字列表和字符串不是等价的,字符串常量包含一个额外的终止空字符。如:
      const char ca1[] = {'a','b','c'};//维数为3
      const char ca2[] = "abc"; //维数为4,包括一个终止空字符
 
  3.数组与指针类型的关系,如:
   int ia[] ={1,2,3,4,5};
   ia; //返回数组的第一个元素的地址
   &ia[0]; //返回数组的第一个元素的地址
   *ia;//返回数组的第一个元素的值
   ia[0];//返回数组的第一个元素的值
 
  4. 二维数组
   二维数组和一维数组不同,二维数组的数组名不再表示首地址,而是一个指向存放首地址的地址。假设有个二

维数组b:b[M][N]
   该二维数组的首地址为*b,或b[0],第1个元素的值为元素的地址**b或为*b[0]。
    第i行第0列的元素的地址用指针表示为*(b+i),该行中第j列元素的地址用指针表示为*(b+i)+j,那么,二维数

组元素b[i][j]的值用指针表示为*(*(b+i)+j)。

 vector容器类型:vector类是随着标准C++引入的标准库的一部分,为了使用vector,我们必须要包含相关的头文

件,#include<vector>
  对vector有两种不同习惯的用法:内置数组习惯用法和STL习惯用法。
  定义一个已知长度的vector :vector<int> ivec(10);这个与如下定义一个包含十个元素的内置数组相似。如:

int ar[10];
  内置数组习惯用法:
    int ia[4] = {1,2,3,4,};
  初始化:1)对于内置数组,我们可以显示的把数组的元素初始化为一组常量值,但是我们不能用同样的方法初

始化vector;但是我们可以将vector初始化为一个已有数组的一部分或全部;如 vector<int> ivec(ia, ia+4);//

全部
     vector<int> ivec(&ia[2], &ia[5]);//一部分
          2)与内置数组不同,vector可以被另一个vector初始化,或被赋值给另一个vector,如 vector<int>

sevc_;
     vector<int> ievc(ia);
     sevc_ = ievc;
  STL习惯用法:
  1)不是定义一个已知大小的vector,而是定义一个空的vector。
               如:vector<string> text;然后向vector中插入元素,而不再是索引元素,以及向元素赋值。如

:string word; cin >> word;text.push_back(word);
  2)如何遍历呢?一个是:for(int ix = 0; ix = text.size(); ++ix){
                           cout << text[ix] << ' ';
                        }
     另一个是:使用vector操作集中的begin()和end()所返回的迭代器iterator,iterator是标准库中的类,它具

有指针的功能。如:
           for(vector<string>::iterator it = text.begin(): it != text.end:++it)
          {
              cout << *it << ' ';
           }
 复数:使用复数的头文件:#include <complex>
 typedef:
 volatile:
 pair:
                                 
                                    表达式

 1.显式类型转换或强制类型转化:static_cast<int> (byte_value)//char byte_value = 32;
 
 2.sizeof操作符: 返回值类型为size_t   #include<cstddef>

 3.new和delete表达式:new表达式返回指向新分配的对象的指针。
   1)int *pi = new int;从空闲存储区中分配了一个int型的对象,并用它的地址初始化pi;在空闲存储区内实

际分配的对象并没有被初始化。
   2)int *pi = new int(1024);它不但分配了一个对象而且用1024将其初始化。
   3)int *pi = new int[10];从空闲存储区中分配了一个数组,其中含有10个int型对象,并用它的地址初始化

pi,为数组的元素没有被初始化。
   4)new表达式并不返回实际分配的对象,而是返回这个对象的地址。
   5)delete显示的将对象的内存返还给空闲存储区。
 4.逗号表达式:从左到右计算,结果是最右边表达式的值。

                             
                                   语句
  if语句、switch语句、for循环语句、while语句、do while语句、break语句、continue语句、goto语句


                                   抽象容器类型

 1.顺序容器:vector、deque、list(都是动态增长的)
   1)vector:表示一段连续的内存区域,每个元素被顺序存储在这段内存中。对vector的随机访问效率很高,因

为每次访问离vector起始处的位移都是固定的。但是在任意位置而不是在vector末尾插入元素,则效率很低,因为

它需要把待插入的元素右边的每个元素都拷贝一遍。同样任意删除某个位置的元素也是,效率很低。
   2)deque:也表示一段连续的内存区域,但是与vector不同的是,它支持高效的在其首部插入和删除元素。
   3)list:它表示非连续的内存区域,它是通过一对指向首尾元素的指针双向链接起来,从未允许向前和向后遍

历,在任意位置插入和删除元素的效率都是很高的。

 2.关联容器:map、set
   1)map:是个键值对,键用来索引map,值用作被存储和检索数据。头文件:#include<map>
     如:string query("pickle"); 
         vactor<location> *locat; 
         locat = text_map[query];
   2)定义一个map:
       map<string, int> word_conut; 
       class employee;
       map<int, employee*> personnel;它由一个int作为索引并拥有相关联的指向雇员类实例的指针。
   3)向map插值:map定义了一个value_type来表示相关的键值对。
           word_count.insert(map<string, int>::value_type(string("Anna"),1));其作用是创建一个pair对

象,接着将其直接插入map。为了便于阅读,我们使用typedef:typedef map<string, int>::value_type

valType;使用它插入操作就比较简单了;word_count.insert(valType(string("Anna"),1));
           为插入一定范围内的键值元素,我们可以用另一个版本的insert()方法,它用一对iterator作为参数

。map<string, int> word_count; map<string, int> word_count_two;
word_count_two.inset(word_count.begin(),word_count.end());或者map<string, int> word_count_two

(word_count);
 

------------------------------------------------------------------
常量指针:
   const int *point;指向的对象是常量。
   *point是常量
指针常量:
   int const *point;指针本身就是常量,再定义指针常量时必须初始化。
   point是常量
常量指针常量:
   const int const *point;
   *point和poing都是常量。
---------------------------------------------------------------------------------------

内联函数:我们常常喜欢定义一个小函数执行简单的功能,如  int min( int v1, int v2){return v1<v2<<v1:v2

)},但是这个函数有个缺陷:调用函数比直接计算条件操作符要慢的多,不但必须拷贝两个实参,保存机器的寄存

器,程序还必须转向一个新的位置,同此,手写的条件操作符能快的多,inline内联函数给出一个解决办法,如一

个函数被定义为内联函数,那它将在程序中每个调用点上被内联的展开。

链接指示符:extern "C"
    程序员使用链接指示符告诉编译器,该函数使用其他的程序设计语言编写的。链接指示符有两种形式,即单一

语句,也可以是复合语句。
    单一语句: extern "C" void exit(int);
    复合语句:extern "C" { int printf(const char* ...);
                           int scanf (const char * ...); }
    连接指示符不能出现在函数体中。

------------------------------------------------------------------------------------
指向函数的指针的类型:
 int lexicoCompare (const string &s1, const string &s2){return s1.compare(s2);}
 指向函数lexicoCompare()的指针必须指向与lexicoCompare()相同类型的函数。
  int (*pf)(const string &s1, const string &s2){return s1.compare(s2);}这句声明了pf是一个指向函数的

指针。
   指向函数的指针可如下被初始化:
        int (*pfi)(const string &, const string &) = lexicoCompare;
        int (*pfi2)(const string &, const string &) = &lexicoCompare;
   指向函数的指针可以如下被赋值:
        pfi = lexicoCompare;
        pfi2 = pfi;
   函数指针可以赋值为0,表示不指向任何函数。

函数指针的数组:int (*testCases[10])();testCases声明为一个拥有10个元素的数组。每个元素都是一个指向函

数的函数指针。


---------------------------------------------------------------------------
异常:
  void (*pf) (int) throw(string);该声明表示pf是一个函数指针,它只能抛出string类型的异常。和函数声明

一样,同一指针的不同异常规范不能累积,指针pf的所有声明都必须指定相同的规范。如 void (*pf) (int),这个

是错误的,缺少异常规范。
--------------------------------------------------------------------------
                         
                          基于对象的程序设计
string::size_type:任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要

把size的返回值赋给一个int变量。虽然不知道string::size_type的确切类型,但可以知道它是unsigned型。对于

任意一种给定的数据类型,它的unsigned型所能表示的最大正数值比对应的signed要打。所以说size_type存储的

string长度是int所能存储的两倍。

访问限定符:public  private  protected
  public:在程序的任何地方都可以被访问。
  private:只能被成员函数和类的友元访问。
  protected:对派生类就像public成员一样,对其他程序则表现的像private。

inline和非inline成员函数:
  在类体中声明并同时定义的函数被称为在类定义中定义的内联函数。

const和mutable(易变的):
   声明为const的成员函数,定义时不能修改类成员数据。为了允许修改一个类的数据成员,即使它是一个const

对象的数据成员,我们也可以把该数据成员声明为mutable。mutable数据成员永远不会是const成员,即使它是一

个const对象的数据成员。mutable成员总可以被更新,即使是在一个const的成员函数中。
    为了声明一个mutable的数据成员,我们必须把关键字mutable放在类成员表中的数据成员声明之前。
  如:class Screen {
            public:
                void move(int x, int y) const;
            private:
                mutable string::size_type _cursor;
      }

静态类成员: static const
   在类体内初始化一个const静态数据成员时,该成员必须仍然要被定义在类定义之外。但是,因为这个静态数据

的成员的初始值是在类体中指定的,所以在类定义之外的定义不能指定初始值。因为name是个数组(不是有序类型)

,所以它不能在类体内被初始化、任何试图这么做的行为都会导致编译时刻错误。
 如:// 头文件
     class Account{
        private:
           static const int nameSize = 16;
           static const char name[nameSize];
      };
     文本文件
     const int Account::nameSize;//必需的成员定义
     const char Account::name[nameSize] = "Savings Account";

  静态成员的唯一性本质使他能够一独特的方式使用,但是对于非静态成员来说却是错误的不允许的。1,静态数

据成员的类型可以是其所属类,而非静态成员只能被声明为该类的对象的指针或引用。2,静态成员可以作为类成

员函数的缺省实参,而非static成员不能。


指向类成员的指针:
  指向类成员数据的指针:short Screen::*ps_Screen = &Screen::_height;
  指向类成员函数的指针: int (Scrren::*pmfi)() = &Screen::height;
  指向静态类成员的指针和指向非静态类成员的指针的区别:指向静态类成员的指针不需要类对象,如:double

*pd = &Account::_interestRate;而不是double (Account::*pd) = &Account::_interestRate;

----------------------------------------------------------------------------------------------

CFileFind类:使用的头文件是,afx.h
  MFC类CFileFind执行本地文件查找,是CGopherFileFind和CFtpFileFind的基类;后两类用于Internet文件查找

。CFileFind包括的成员函数有开始查找、定位文件、返回标题名或路径。对于Internet查找来说,GetFileURL返

回文件的URl。
 如:以下代码将当前目录下的文件枚举出来并打印每个文件名:   
     CFileFind finder;   
     BOOL bWorking = finder.FindFile("*.*");   
     while(bWorking)   
     {   
       bWorking = finder.FindNextFile( );   
       cout <<(LPCTSTR) finder.GetFileName( ) <<endl;   
     }

  CFileFind属性:
  GetLength 获取找到文件的长度,以字节为单位   
    GetFileName 获取找到文件的名字       GetFilePath 找到文件的全路径   
    GetFileTitle 获取找到文件的标题,标题不包括扩展内容       GetFileURL 获取找到文件的URL,包括文

件路径   
    GetRoot 获取找到文件根目录   
    GetCreationTime 获取文件创建时间   
    GetLastAccessTime 获取文件最后一次打开的时间   
    GetLastWriteTime 文件最后改变和存储的时间   
    MatchesMask 指定要找的文件的属性   
    IsDots 查看文件名是否是包含"."或"..",以表明它的确是一个目录   
    IsReadOnly 文件是否是只读   
    IsDirectory 文件是否是目录       IsCompressed 文件是否是压缩   
    IsSystem 文件是否是系统文件   
    IsHidden 文件是否是隐藏       IsTemporary 文件是否是临时的   
    IsNormal 文件是否是常规的(无其它属性)   
    IsArchived 文件是否是档案

函数CreateDirectory和RemoveDirectory函数:创建和删除目录,创建的和删除的只能是空目录,一定要注意
DeleteFile函数:删除文件

INVALID_FILE_ATTRIBUTES == GetFileAttributes(lpszDirPath)
      || FILE_ATTRIBUTE_DIRECTORY != GetFileAttributes(lpszDirPath):判断指定路径是否

非法,是否存在。
      

-----------------------------------------------------------------------------------------------
union联合:union的成员可以声明为公有、私有或保护的,但是不能有静态数据成员或是引用成员。如果一个    

      类类型定义了构造函数,析构函数或者拷贝赋值操作符,则它不能成为union的成员类型。

局部类:在函数体内定义的类称为局部类。局部类不能被外部所继承。局部类中不能有静态成员函数,并且多    

   有成员函数都必须定义在类体内。

嵌套类:
    1.在一个类中定义的类称为嵌套类,定义嵌套类的类称为外围类。之所以使用嵌套类,往往是因为外围      

  类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该        

底层实现。
    2.嵌套类是一个独立的类与外围类不相关。它的成员不属于外围类,外围类的成员也不属于嵌套类;外围
      类对嵌套类成员的访问没有任何特权,嵌套类对外围类成员的访问也同样如此。
   注意:嵌套类被看到之前我们只能声明嵌套类的指针和引用。如下:
        class A
 {
     public:
             A();
            ~A();
             void operate();
     private:
             class B;
            B *m_b;
 };
        #include "nestclass.h"
        #include <iostream>
        using namespace std;
        class A::B
        {
            public:
               B(){}
               ~B(){}
               void operate()
               {
                    cout<<"B operate!"<<endl;
               }
        };
        A::A()
        {
        }
        A::~A()
        {
        }
        void A::operate()
        {
            m_b = new B;
            cout<<"A operate!"<<endl;
            m_b->operate();
        }
       #include "nestclass.h"
       void main()
       {
           A a;
           a.operate();
        }


------------------------------------------------------------------------------------------------

                                 类的初始化

1.把一个类对象插入到每一个容器中都是通过拷贝构造函数来实现的。
2.构造函数自动执行,只要创建该类型对象,编译器就会运行一个构造函数。
3.与其他函数不同的是,构造函数也可以包含一个构造函数初始化列表。
4.可以初始化const对象或引用类型的对象但不能对他们赋值,可以使用初始化列表对他们初始化。
5.static是类的组成部分,表示任何对象的组成部分;static成员函数不能声明为const,因为将其声明为  const

就意味着不能修改该函数所属的对象;static成员函数也不能声明为虚函数。
6.static数据成员,可以声明为任意类型,常量、引用、数组、类类型等等。
7.const static数据成员在类的定义体中初始化时,该数据成员仍必须在类的提议体外定义,但在内部进行初  始

化了,那么在外部就不需要再指定初始值了。
                 
                            复制构造函数、析构函数、赋值操作符

复制构造函数:只有一个形参,该形参是对本类类型对象的引用(用const修饰)。

string (const &string)

string empty_copy = string();先创建一个临时对象,然后复制构造函数用该对象初始化empty_copy。


基类和派生类的构造:构造函数的调用顺序,
 1,基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在    

成员初始化表中的顺序。
 2,成员类对象的构造函数。如果有多个成员类对象,则构造函数的调用顺序是对象在类中被声明的顺序,而    

不是它们出现在成员初始化表中的顺序。
 3,派生类的构造函数。派生类构造函数应该不能直接向一个基类数据成员赋值,而是把值传递给适当的基类
    构造函数。
 
 注意:1.****派生类并没有继承基类的构造函数。****
       2.****派生类构造函数只能合法地调用其直接基类的构造函数。****
       3.****更一般情况下,派生类的析构函数调用的顺序与它的构造函数调用顺序相反。****


                                  虚函数
1.保留字virtual是声明虚函数的。除了构造函数之外,任意非static成员函数都可以是虚拟的。保留字
  virtual只能在类内部声明时可以出现,不可在类定义体外的函数定义上。
2.纯虚拟函数的声明个虚拟函数的声明区别是,虚拟函数声明后面跟赋值0就是纯虚拟函数了。
  如:virtual ostream& print( ostream& = cout) const = 0;
3.抽象类:包含(或继承)一个或者多个纯虚拟函数的类被编译识别为抽象基类。抽象基类只能出现在后续的派
  生类中。

*****
*****
*****
4.虚拟函数和缺省实参:通过基类指针或者引用调用派生类实例,则传递给它的缺省实参是由基类指定的。
  那么为什么还要在派生类中指定缺省实参呢?我们可能希望有不同的缺省实参,不是根据被调用函数的特定
  子类型,而是根据“调用该函数时所使用的指针或引用”的类型。

 

posted on 2012-11-05 10:26  ssy黑桃a  阅读(630)  评论(0编辑  收藏  举报