Fork me on GitHub

C++基础教程(总结)

1、C++语言支持的新特性

  

  注:1983年夏,“带类的C”被正式命名为“C++”

2、常量

  数值常量、字符常量、符号常量、枚举常量。。。
  const常量需初始化,否则其值为一个随机数,以后不可赋值
3、变量
  • 变量名代表内存中的一个存储单元,在程序编译连接时由系统给每一个变量分配一个地址。通过变量名找到相应的存储单元,从中读取数据
  • 变量名的命名规则
  • 未对变量赋初值,其值是一个不可预测的值
  • 初始化不是在编译时完成的(静态存储变量和外部变量除外),而是在运行时执行本函数时赋予初值的
  • 程序的局部变量存在于( 栈 )中,全局变量存在于( 静态区 )中,动态申请数据存在于( 堆 )中
  • 变量命名规则:匈牙利命名法(Hungarian)、驼峰命名法(cameraCase)、帕斯卡命名法(PascalCase)
4、数据类型
 
  
  • C++编译系统把十进制小数形式的浮点数默认按双精度处理
  • 浮点数在内存中以指数形式存放
  • 若一个字符串的最后一个为“\”,则它是续行符
  • 在计算机中负数的运算和显示以补码的形式,16的原码为 0001 0000,~16为 1110 1111 ,则~16为负数。因此,~16补码为 1000 1010+1=1000 1011=-11

不同类型数据间的转换(赋值兼容):

  • 标准类型数据间的转换
  • 隐式转换;显示转换:类型名(数据) / (类型名)数据
  • 用转换构造函数进行类型转换
  • 用类型转换构造函数进行类型转换
 下面为常量、变量以及数据类型的测试代码:
#include<iostream>
using namespace std;
#define PI 3.1415926
int main()
{
    const int NUM=6;
    enum color{red,yellow,green,black,white};
    cout<<green<<endl;

    int a;//未初始化,值不确定
    cout<<"a="<<a<<endl;
    float b=1.23456789;
    cout<<b<<endl;
    long double c=1.23456789;
    cout<<c<<endl;
    cout<<sizeof(short int)<<"Byte"<<'\t'
        <<sizeof(int)<<"Byte"<<'\t'
        <<sizeof(float)<<"Byte"<<'\t'
        <<sizeof(double)<<"Byte"<<'\t'
        <<sizeof(long double)<<"Byte"<<endl;

    double d=1.23f;
    cout<<d<<endl;

    char ch=6;
    cout<<ch<<endl;

    char ch1=0141;  //97
    cout<<"ab\\\"cd\"\t\101\x41\0cd"<<'\0'<<ch1<<endl;
    return 0;
}

 5、指针

  • 一个变量的地址即该变量的指针
  • 指针变量的值是地址(指针)
  • 凡是存放在内存中的程序和数据都有一个地址,可以用它们占用的那片存储单元中的第一个存储单元的地址表示
& 和 * 的使用:
  
 
有关指针的数据类型:
  
 
指针运算:
  • 指针变量加减一个整型值
  • 指针变量的赋值
  • 指针变量可以赋空值,但不能赋整数
  • 指向同一类型的指针变量相互赋值时,后期操作可能造成指针悬挂问题(一指针所指空间被释放,另一指针所指空间亦被释放),在释放一指针所指的空间时,一定要确保所有指向该空间的指针都已不再需要,以防止指针悬挂   
  • 若要对不同类型的指针变量赋值,应用强制类型转换
  • 指针变量的比较
  • 指针变量的相减,相加无意义
指针的一些注意事项:
  • 所有指针,包括访问本地堆的指针,在 Win32 下都是 32 位(4个字节)
  • 一般的C++编译系统为每一个指针变量分配4个字节的存储单元,用来存放变量的地址
  • 指针未初始化为野指针,在使用指针变量之前须先给它赋一个指向合法具体对象的地址值,否则可能破坏有用的单元的内容
指针的优缺点:

优点

  • 在调用函数时,若改变被调用函数中某些变量的值,这些值能为主调函数使用,可通过函数的调用得到多个可改变的值
  • 可以实现动态存储分配
  • 占内存少,运算速度快

缺点

  • 容易产生难以发现的错误
数组与指针:
  
 
指针数组作为main函数的形参:
 
  在DOS状态下输入:命令名  参数….
 
下面为相应的代码:
#include<iostream>
using namespace std;
int main(int argc, char *argv[])
{
    while(argc > 1)
    {
        ++argv;
        cout<<*argv<<endl;
        --argc;
    }
    return 0;
}

 动态存储分配:

  

6、引用
  • int &b = a;    //声明b是a的引用(别名
  • 声明引用时必须使之初始化
  • 在任何情况下都不能使用指向空值的引用,一个引用必须总是指向某些对象
  • 声明b是a的引用后,b不能再作为其他变量的引用,且b和a占同一存储单元,具有同一地址(指针变量需另外开辟空间)
  • 主要用来作为函数参数
  • 调用函数时,实参是变量名不是地址,而系统向形参传递的是地址不是其值
  • const &常用于修饰函数形参,提高效率
7、字符串
  • 对一个字符串常量,系统会在所有字符的后面加一个’\0’作为结束符
  • 字符串的长度比对应的字符数组(包含一个空字符’\0’)少1字节,字符串数组每一个字符串元素不包含’\0’
  • 编译系统为每一个字符串变量(string类的一个对象)分配固定长度的存储单元(VC为16个字节,GCC为4个字节),存放字符串的地址,即字符串的指针
  访问字符串的三种方法:
  1. 字符数组,即C-string方法
  2. 字符串变量,即string方法(C++)
  3. 字符指针

指针、引用、字符串测试源代码:

#include<iostream>
using namespace std;
int main()
{
    char str[] = "Hello";
    string m_str = "Hello";
    string name[5] = {"zhang","li","gao"};
    char *pstr = "Hello";

    while(*pstr!='\0')
    {
        cout<<*pstr;
        pstr++;
    }
    cout<<endl;
    
    cout<<"字符数组的长度:"  <<sizeof(str)    <<"Byte"<<endl;
    cout<<"字符串的长度:  "  <<strlen("Hello")<<"Byte"<<endl;
    //每一个字符串变量被编译系统分配固定长度的存储单元,VC++为16个字节
    cout<<"字符串变量的长度:"<<sizeof(m_str)  <<"Byte"<<endl;
    cout<<sizeof(string)<<'\t'<<sizeof(name)<<endl;
    //一般的C++编译系统为每一个指针变量分配4个字节的存储单元,用来存放变量的地址
    cout<<sizeof(pstr)<<endl;

    cout<<"**************************************************"<<endl<<endl;

    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    int *p=arr;
    for(int i=0;i<10;i++)
    {
     // cout<<arr[i]<<'\0';
        cout<<*(p+i)<<' ';
    }
    cout<<endl;

    int *p1 = new int(10);
    cout<<*p1<<endl;
    delete p1;

    return 0;
}

8、函数

  • 函数名代表函数的入口地址函数的指针,函数的指针在编译时被分配
  • 当数组作函数形参时,C++编译系统将形参数组名一律作为指针变量来处理
  • 实参数组名代表一个固定的地址,或说是指针常量,而形参数组名为指针变量
  • 根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数
  • C++里面如何声明const void f(void)函数为C程序中的库函数? 在该函数前添加extern“C”声明
  • 若函数参数是指针,且用于输入,则应在类型前加const
  • 在函数参数的入口处,要使用断言检查参数的有效性
函数参数传递:
  
 
内置函数:
  • 在编译时将调用的函数的代码直接嵌入到主调函数中,而不是将流程转出去,这种嵌入到主调函数中的函数称为内置函数(inline function),又称内嵌(内联)函数
  • 在函数首行的左端加一个关键字inline,可在声明和定义时同时写,也可只在其中一处写,效果相同
  • 节省运行时间,但增加了目标程序的长度
  • 只有那些规模小而又被频繁调用的简单函数,才适合声明为inline函数;而且对函数作inline声明对编译系统只是建议性的
  • C++使用内联函数代替宏定义
函数重载、覆盖与隐藏:
  
 
9、枚举
enum weekday{sun, mon, tue, wed, thu, fri, sat};//声明
weekday workday, week_end;   //定义枚举变量
在C++中允许不写enum(一般也不写),花括号中的称为枚举元素枚举常量,C++编译按定义时的顺序对它们赋值为0,1,2,3,…
 
可在声明枚举类型时指定枚举元素的值:
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat};

编译系统自动按顺序加1,则sat为6

一个整数不能直接赋给一个枚举变量,应先强制类型转换,例:workday = weekday(2); //C++风格形式
 
10、共用体(联合)
  
 
11、结构体
  
 
12、位段(位域)
  
 
枚举、共用体、结构体、位段源代码:
#include<iostream>
using namespace std;

//声明枚举类型
enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat};
struct Student  //结构体
{
    int age;
    char name[20];
    
    union P     //共用体
    {
        int grade;
        char position[10];
    }category;

    unsigned a:2;//位段
    unsigned b:3;
};
int main()
{
    weekday workday;//定义枚举变量
    workday=weekday(1);//C++风格的强制类型转换格式
    cout<<workday<<endl
        <<wed<<endl;

    Student person[2];//定义共用体变量
    person[0].category.grade=20;
    cout<<person[0].category.grade<<endl;

    person[1].a = 3; //给位段赋值
    cout<<"a="<<person[1].a<<endl;
    
     person[1].b = 12;
     //十进制12-->二进制1100--取其低3位-->二进制100-->十进制4
    cout<<"b="<<person[1].b<<endl;

    return 0;
}

 13、类

  • 类是对象的抽象,不占存储空间;对象是类的实例,占用存储空间
  • 一个对象所占的空间的大小只取决于其中的数据成员所占的空间,而成员函数不占用对象的存储空间
  • 类中普通函数不占内存,只有虚函数(无论多少)会占用一个指针大小的内存
  • 信息隐蔽 = 类的公用接口与私有实现的分离
  • 把类的声明(包含成员函数的声明)放到一个头文件中;把成员函数的定义放到另一文件中,单独编译,从而不重复编译
  • 类库 = 类声明头文件 + 已编译过成员函数的定义的目标文件
  • 不要随便的将构造和析构函数放在类声明中
类的声明与对象的定义:
  
 
成员对象的访问:
  
 
构造函数:
  
 
析构函数:
  • ~Box( ){ };
  • 析构函数与类同名,无返回值(类型),无参数
  • 一个类只能有一个析构函数
  • 当对象的生命周期结束时,自动执行析构函数
  • 对一个对象来说,析构函数是最后一个被调用的成员函数
  • 其作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作
  • 调用析构函数的顺序与构造函数相反
 
对象指针:
  
 
类和对象源代码(一):
#include<iostream>
using namespace std;
class Box
{
    int length, width, height;//私有成员
public:
    Box();
    //用参数初始化表对数据成员初始化
    Box(int len, int w, int h):length(len),width(w),height(h)
    {    
    }
    ~Box()
    {
    }
    
    int volume();
};//分号不可少
Box::Box()
{
    length = 5;
    width  = 5;
    height = 5;
}
int Box::volume()
{
    //显示的使用this指针
    return this->width * this->height * this->length;
}

int main()
{
    Box b1;
    Box *p = NULL;//指向对象的指针
    //定义对象数组
    Box b[3] = {Box(),Box(10,10,10),Box(20,20,20)};
    p = &b[2];

    cout<<b1.volume()  <<endl;
    
    cout<<b[0].volume()<<'\t'
        <<b[1].volume()<<endl;
    
    cout<<p->volume()  <<'\t'
        <<(*p).volume()<<endl;

    return 0;
}

 

共用数据的保护:

  

静态成员:

  

对象的赋值与复制:

  

友元:

  

类和对象源代码(二):

#include<iostream>
using namespace std;
class Date;//对Date类的提前引用声明
class Time
{
public:
    Time(int, int, int);
    void display(Date &);//形参为对象的引用
private:
    int hour,minute,second;
};
class Date
{
public:
    Date();
    Date(int, int, int);
    //声明Time类中的display函数为Date类的友元成员函数
    friend void Time::display(Date &);
private:
    int year,month,day;
};

Time::Time(int h, int m, int s):hour(h),minute(m),second(s)
{
}
void Time::display(Date &d)
{
    cout<<"d.day="<<d.day<<'\t'
        <<"t.hour="<<hour<<endl;
}
Date::Date()
{
    year  = 1000;
    month = 12;
    day   = 20;
}
Date::Date(int y, int m, int d)
{
    year  = y;
    month = m;
    day   = d;
}

int main()
{
    Time t(5,5,5);
    Time t1(t); //对象的复制
    Date d(2000,10,30), d1;
    d1 = d;     //对象的赋值
    t.display(d);
    t1.display(d1);
    
    return 0;
}

14、运算符重载

  • 实质上是函数重载
  • 形式:函数类型 operator运算符名称(形参表列){};
  • C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载
  • 重载不能改变运算符运算对象的个数、优先级别、结合性,不能有默认的参数,参数至少有一个类对象(其引用)
  • C++约定:若在前置自增(自减)运算符重载函数中增加一个int型形参,就是后置(自减)运算符函数
  • 只能将重载“>>”和“<<”的函数作为友元函数或普通的函数,而不能将他们定义为成员函数

15、继承和派生

  • 继承:一个新类从已有的类那里获得其已有特性
  • 派生:从已有的类(基类)产生一个新的子类(派生类)
  • 继承与组合:在一个类中以另一个类的对象作为数据成员
  • 继承分单继承和多重继承,派生分一级派生和多级派生
  • 继承方式:公用、保护、私有
  • 访问属性:公用、保护、私有、不可访问
  • 派生类的成员:基类的全部成员(不包括构造和析构函数)+自己增加的成员
  • 私有成员只能被本类的成员函数访问,保护成员可被派生类的成员函数引用
  • 类的成员在不同的作用域中有不同的访问属性
  
 
派生类的构造与析构函数:
  • 派生类构造函数名(总参数表列):基类构造函数名(参数表列),…,子对象名(参数表列),…  { 派生类新增数据成员的初始化语句 }
  • 派生类构造函数的执行顺序:基类、子对象、派生类
  • 多重继承中的初始化是按基类表中的说明顺序进行的
  • 析构函数与构造函数相反
  • 若基类未定义构造函数或定义了未带参数的构造函数,则在派生类中可不写基类构造函数;在调用派生类构造函数时系统会自动首先调用基类默认的构造函数
多重继承引起的二义性问题:
  
 
基类与派生类的转换:
  
 
继承与派生源代码:
#include<iostream>
#include<string>
using namespace std;

class Person
{
public:
    Person(string nam,int a,char s):name(nam),age(a),sex(s)
    {
    }
    void show()
    {
        cout<<"name:" <<'\t'<<name <<endl
            <<"age:"  <<'\t'<<age  <<endl
            <<"sex:"  <<'\t'<<sex  <<endl;
    }
protected:
    string name;
    int age;
    char sex;
};

class Teacher:virtual public Person//声明Person为公用继承的虚基类
{
public:
    //构造函数
    Teacher(string nam,int a,char s,string t):Person(nam, a,s),title(t)
    {
    }
    void display()
    {
        show();
        cout<<"title:"<<'\t'<<title<<'\n';
    }
protected:
    string title;
};
class Student:virtual public Person//声明Person为公用继承的虚基类
{
public:
    //构造函数
    Student(string nam, int a,char s,float sco):Person(nam,a,s),score(sco)
    {
    }
    void display()
    {
        show();
        cout<<"score:"<<'\t'<<score<<'\n';
    }
protected:
    float score;
};

class Graduate:public Teacher,public Student//声明多重继承的派生类Graduate
{
public:
    //不仅要对直接基类初始化,还要对虚基类初始化
    Graduate(string nam,int a,char s,string t,float sco,float w):
      Person(nam,a,s),Teacher(nam,a,s,t),Student(nam,a,s,sco),wage(w)
     {
     }
     void present()
     {
        cout<<"name:" <<'\t'<<name <<'\n'
            <<"age:"  <<'\t'<<age  <<'\n'
            <<"sex:"  <<'\t'<<sex  <<'\n'
            <<"title:"<<'\t'<<title<<'\n'
            <<"score:"<<'\t'<<score<<'\n'
            <<"wage:" <<'\t'<<wage <<'\n';
     }
private:
    float wage;
};


int main()
{
    Graduate grad1("WangLi",24,'f',"assistant",99.5,1325.5);
    grad1.present();
    
    return 0;
}

16、多态性和虚函数

  • 多态性:用一个函数名调用具有不同内容(功能)的函数;不同对象对同一消息有不同的响应方式(一个接口,多种方法)
  • 从系统实现的角度,多态性分为:静态多态性(编译时的多态性)和动态多态性(运行时的多态性,通过虚函数实现)
  • 关联:调用具体的对象的过程(把一个函数名与一个对象捆绑在一起)
  • 静态(早期)关联:通过对象名调用虚函数,在编译时即可确定其调用的函数属于哪个类
  • 动态(滞后)关联:通过基类指针调用虚函数,在运行阶段把虚函数和类对象捆绑在一起

虚函数:

  • 类外定义虚函数时不必加virtual
  • 派生类中定义的同名函数:函数名、类型、参数个数、参数类型须与虚函数相同;当一个成员函数被声明为虚函数后,其派生类的同名函数都自动成为虚函数
  • 先用基类对象指针指向同一类族中某一对象,即可调用该对象中的同名函数
  • 函数重载处理的是同一层次上的同名函数问题,而虚函数处理的是不同派生层次上的同名函数问题(函数覆盖)
  • 当一个类有虚函数时,编译系统会为该类构造一个虚函数表(一个指针数组),存放每个虚函数的入口地址
  • 构造函数不能声明为虚函数;若基类的析构函数声明为虚函数,则其所有派生类的析构函数均为虚函数
纯虚函数与抽象类:

纯虚函数:virtual 函数类型 函数名(参数表列)=0;//声明语句

  • 无函数体;“=0”并不表示返回值为0,只是形式;不具备函数的功能,不能被调用
  • 若在派生类中未对该函数定义,则在派生类中仍为纯虚函数

抽象类:不用来定义对象,只作为一种基本类型用作继承的类

  • 可定义指向抽象类对象的指针变量,通过指针调用具体派生类的虚函数
  • 凡是包含纯虚函数的类都是抽象类,抽象基类是本类族的公共接口
多态性与虚函数源代码:
#include<iostream>
#include<string>
using namespace std;

class Student
{
public:
    Student(string nam,int a,char s,float sco):name(nam),age(a),sex(s),score(sco)
    {
    }
    
//    virtual void display() = 0;//纯虚函数
    virtual void display()//虚函数
    {
        cout<<"name:" <<'\t'<<name <<'\n'
            <<"age:"  <<'\t'<<age  <<'\n'
            <<"sex:"  <<'\t'<<sex  <<'\n'
            <<"score:"<<'\t'<<score<<'\n';
    }
protected:
    string name;
    int age;
    char sex;
    float score;
};

class Graduate:public Student
{
public:
    Graduate(string nam,int a,char s,float sco,float w):
      Student(nam,a,s,sco),wage(w)
     {
     }
     void display()//虚函数
     {
        cout<<"name:" <<'\t'<<name <<'\n'
            <<"age:"  <<'\t'<<age  <<'\n'
            <<"sex:"  <<'\t'<<sex  <<'\n'
            <<"score:"<<'\t'<<score<<'\n'
            <<"wage:" <<'\t'<<wage <<'\n';
     }
private:
    float wage;
};


int main()
{
    Student  stud("LiGang",18,'m',95);
    Graduate grad("WangLi",24,'f',99.5,1325.5);
    
    grad.display();//静态关联
    cout<<"******************************************\n";

    Student *pt = &stud;
    pt->display();//动态关联
    
    cout<<"******************************************\n";
    
    pt = &grad;
    pt->display();
    
    return 0;
}

17、输入输出流

  • 标准I/O:键盘输入数据,显示到屏幕上
  • 文件I/O:以外存磁盘文件为输入输出对象
  • 字符串I/O:对内存中指定的空间(通常为字符数组,利用该空间可存储任何信息)进行输入输出
标准输入输出:
  • 标准输出流
  • cout,cerr,clog流
  • 格式输出
  • 使用控制符控制输出格式:头文件iomanip
  • 使用流对象的成员函数:格式标志在类ios中被定义为枚举值
  • 用流成员函数put输出字符
  • 标准输入流
  • cin流
  • 用流成员函数输入字符:无参、1个、3个参数的get,getline
  • Istream类的其他成员函数

18、用typedef声明类型

  

19、预处理命令

  

  

typedef与预处理命令源代码:

#include<iostream>
using namespace std;

typedef int ARR[2]; //用typedef声明数组类型

#define MAX(x,y) x>y?x:y  //带参数的宏定义

int main()
{
    int max;
    ARR a;
    a[0]=1;
    a[1]=2;
    max=MAX(a[0],a[1]);
#ifdef MAX
    cout<<"max="<<max<<endl;
#else
    cout<<"MAX(x,y) is not defined! "<<endl;
#endif
    return 0;
}

20、模板

  

模板源代码:

#include<iostream>
using namespace std;

//声明类模板,Box为类模板名
template<typename TYPE> //<>中的为模板形参表
class Box//类声明
{
    TYPE length, width, height;
public:
    Box();
    Box(TYPE len, TYPE w, TYPE h):length(len),width(w),height(h)
    {    
    }
    ~Box()
    {
    }
    
    TYPE volume();
};

//类外定义成员函数
template<typename TYPE>
Box<TYPE>::Box()
{
    length = 5;
    width  = 5;
    height = 5;
}
//类外定义成员函数
template<typename TYPE>
TYPE Box<TYPE>::volume()
{
    return width * height * length;
}

//声明定义函数模板
template<typename TYPE>
TYPE max(TYPE a,TYPE b)
{
    return a>b?a:b;
}

int main()
{
    Box<int> b1;//定义类对象
    Box<int> b2(10,10,10);

    cout<<b1.volume()<<endl
        <<b2.volume()<<endl;
    
    cout<<"max="<<max(3.3,6.24)<<endl;

    return 0;
}
posted @ 2014-10-31 20:07  晨光iABC  阅读(9416)  评论(3编辑  收藏  举报