c是c的基础,只有充分掌握c并理解c和c的不同,才能理解和运用c++解决问题

c++数据类型

c中数据类型整体可以分为基本数据类型和构造数据类型

  • 基本数据类型包括:整型,实数型,字符型,空值型
  • 构造数据类型包括数组,结构,指针,枚举,联合

c++在c的基础上对数据类型进行了拓展,新增了基本数据类型中的逻辑类型,构造数据类型中的引用类型
以及新增了一个大类:类数据类型

基本数据类型

  • 字符:char, unsigned char
  • 整型(普通,长,短):int, unsigned, long, unsigned long, short, unsigned short
  • 浮点(实数)型(单,双精度):float, unsigned float, double, unsigned double
  • 逻辑类型:bool
    c99借鉴c++加入了bool类型,但c程序中为避免编译环境标准不同等问题一般是将int重命名为bool(但int占用内存大于bool,后者只占1字节)

类型转换:各种类型高低顺序为(低类型->高类型,按占用内存空间大小,高类型转低类型可能丢失数据)

short/char/bool->int->unsigned->long->unsigned >long->float->double->long double

构造数据类型

  • 数组:int data[n]
  • 指针:int *p
  • 结构:struct stu{char name[n];bool sexy;}
  • 联合:union stu{char name[n];bool sexy;}
    • 联合表示几个变量名公用一个内存位置, 在不同的时间保存不同的数据类型和不同长度的变量
    • 联合声明格式和结构体相似,但联合中的数据元素是相互独立的,联合大小由最大的成员的大小来决定(回顾)
  • 枚举:enum color{red,green,blue,yellow}
    • 枚举是自定义的若干枚举常量的集合,每个成员都是一个以标识符形式表示的整型常量(回顾)
    • 默认状态下,常量值就是成员对应的序号
      引用类型:int value;int &p=value; 引用实际上就是给目标变量的内存空间取的别名
  • 类:class

类和结构体的联系和区别

c++不但保留了结构体关键字struct,并且还扩充了其功能

  • 在c语言中,结构体只能包含不同类型的数据变量,相当于只能把几种数据组合起来,表现一个实体的静态属性
  • 在c++中,结构体类似于类,既可以包含数据成员变量,又可以包含成员函数
    • 不仅如此,结构体可以继承派生,甚至有虚函数机制,可以实现多态性
    • 从本质上说c++的结构体和类区别很小,能完成的功能基本相同
    • 结构体在创建和销毁时一样需要调用构造函数和析构函数

二者间的区别主要是:

  • 类中的成员默认是private属性,结构体中的成员默认都是public属性
    • 但是实际写程序一般很少见利用这个特性,因为以降低可读性、容易造成误解为代价换取少声明一个关键字是得不偿失的
  • 类继承默认是private继承,而结构体继承默认是public继承
  • 两者对应的关键字struct和class中只有class可以声明模板参数,而struct不能

c++字符串

c大大增强了对字符串的支持,除了可以使用c语言风格的字符串,还可以使用内置的string类。string类处理起字符串来会方便很多,完全可以代替c语言中的字符数组或字符串指针。string 是c中常用的一个类,使用string类需要包含头文件<string>。

string支持与c语言相同风格的赋值,如:

string str=“hello cpp”; 可以将字符串常量直接赋值给string(使用的是重载的赋值运算符函数)

此外由于string是一个类,因此可以也可以采用对象风格的初始化

string str1(“hello cpp”); 可以直接使用字符串常量初始化string
string str2(5,‘s’); 可以用int、char类型初始化string,表示使用几个相同的字符初始化string,此例为“sssss"
string str3=str1; 可以通过赋值运算符直接用string对象初始化

需要注意的是,string类中有记录字符串长度的成员,因此没有必要在末尾多加一个’\0’。基于这一点来说,一个空的string就是"“而不是”\0",同时声明一个string类而不初始化会调用默认无参构函数将其初始化为"",而不是随机值。string字符串的长度就是字符串本身的长度,而不是像char[]和char*那样是长度+1。

string重载了其’<<‘和’>>'运算符,与c语言风格字符串可以进行同样的输入输出。同时它也重载了[]运算符,可以像c语言风格的字符串一样按照下标来访问和修改其中的每一个字符,如:

string str=“hello”;
str[4]=‘h’;
std::cout<<str[0];

注意string也是存在越界问题的。string本质上是对动态数组的一层封装,在开始的时候根据初始化内容申请适当大小的容量(一般就等于初始化字符串的长度,有些编译器可能会额外多给一点容量),之后只有通过其重载的+=运算符或append()、resize()等成员函数才能扩容,直接下标访问实际上跟c语言的数组一样是基址加偏移量的模式,并不能改变其容量。

string还重载了+和+=的运算符函数,使得其可以非常简单地实现c语言中字符串拼接函数strcat的操作,并且因为string是动态数组,不需要担心空间溢出,如:

string str4=str1+str2;
string str4+=str3;

用+来拼接字符串时,运算符的两边可以都是string字符串,也可以是一个string字符串和一个c风格的字符串,甚至可以是一个string字符串和一个单独的字符(这是因为char*和char可以隐式转换为string)。

string转换为c语言字符串

由于在某些场合下必须使用c语言风格的字符串,最明显的例子就是使用c标准库的函数(c标准库的函数不可能支持string,肯定是拿char*作参数和返回值),此时就涉及到string与char*的互换,如:

string path="/home/hello.c"
fopen(path.c_str(),‘w’);

string类提供了一个转换函数c_str(),该函数能够将string字符串转换为c语言风格的字符串,并返回该字符串的const指针(const char*)

string的增删改查操作

string类中封装了大量的成员函数,可以很方便地完成其作为抽象数据类型所定义的一系列操作。

insert()成员函数可以在string字符串中指定的位置插入另一个字符串,它的一种原型为:

string&insert(size_t pos,const string&str);
pos表示要插入的位置,也就是下标,str表示要插入的字符串,它可以是string字符串,也可以是c语言风格的字符串。

erase()函数可以删除string中的一个子字符串。它的一种原型为:

string&erase(size_t pos=0,size_t len=npos);

pos表示要删除的子字符串的起始下标,len表示要删除子字符串的长度。如果不指明len的话,那么直接删除从pos到字符串结束处的所有字符(len=str.length-pos),前提也是pos不能越界,当pos+len>=str.length时,len=str.length-pos。

substr()函数用于从string字符串中提取子字符串,它的原型为:

string substr(size_t pos=0,size_t len=npos)const; 注意这是个常成员函数,不能改变原string

pos为要提取的子字符串的起始下标,len为要提取的子字符串的长度。与erase()函数的行为类似,最多提取到string的结尾。

以上三个函数的第一个参数pos有越界可能的,如果越界则会产生运行时异常。

find()函数用于在string字符串中查找子字符串出现的位置,它其中的一种原型为:

size_t find(const string& str,size_t pos=0)const; 查找相关的成员函数基本都是常成员函数

第一个参数为待查找的子字符串,它可以是string字符串,也可以是c语言风格的字符串。第二个参数为开始查找的下标,如果不指明,则从第0个字符开始查找。最终返回的是子字符串第一次出现在字符串中的起始下标,如果没有查找到子字符串,那么会返回一个无穷大值4294967295(UINT_MAX)。

rfind()函数与find()功能类似,区别在于rfind的pos参数为查找结束的下标(从第0个字符开始查起),返回值含义与find()相同。
find_first_of()函数用于查找子字符串和字符串共同具有的字符在字符串中首次出现的位置。

c++输入输出

需要在预处理中包含头文件:<iostream> (流库) 注意没有.h,头文件的内容在命名空间std中。<iostream>与c中<stdio.h>非常相像,但二者有本质不同,<stdio.h>是一个函数库的头文件,而<iostream>是一个流对象库的头文件。c++没有新增输入输出语句,而是增加了输入输出库(io库),提供了大量关于输入输出的设施。“流”是一个术语,指从某个io设备上读入或写出数据流,并随着时间顺序生成消耗。输入输出库分为两个基础库:istream(输入流),ostream(输出流)。

c++的基本io对象(4个)

  • 输入流对象(istream对象):针对设备输入操作进行处理的类
  • 输出流对象(ostream对象):针对设备输出操作进行处理的类
  • 输入输出流中包含任意要输出的数据,可以是整型、浮点型、字符串等
  • 系统中预定义流对象:cin,cout
    • cin处理标准输入,即键盘输入istream对象(输入内容)
    • cout处理标准输出,即屏幕输出ostream对象(输出内容)
  • 除此外还有2个基本流对象
    • cerr:标准错误流,用来输出警告和错误信息给程序使用者
    • clog:日志流,用于产生程序执行的一般信息

基本io对象示例:

int main()
    {
        std::cout<<"enter two numbers"<<std::endl;    //可以引入标准命名空间standard(using namespace std),在之后语句省略std
        int v1,v2;                                    //C99以及C++中在首次使用变量之前可在块的任何位置声明变量,不一定需要在复合语句最前面
        std::cin>>v1>>v2;
        std::cout<<"the sum of "<<v1<<" and "<<v2<<" is "<<v1+v2<<std::endl;    //在c++标准输出中操作数可以是全部内置数据类型
        return 0;                                                               //endl表示换行,大体上与'\n'是一样的
    }                                        // 输入:34782 42388 输出结果:the sum of 34782 and 42388 is 77170

注意!!!当输入没有满足程序期望的时候,如:

对于一个int类型的变量a,输入字符H,这种情况下将不会改变变量a的值,并返回0(也就是写入失败)

这一特性经常被用在循环条件中以终止循环,如:

for(;cin>>a;);

在执行程序时系统将io对象与执行窗口关联起来。并且大部分操作系统提供重定向输入输出流的方法,将所选择的文件与流关联起来(对文件中内容进行io)

>>和<<运算符

运算符’<<’,’>>‘将数据与io流对象关联起来,’<<'是预定义的插入符(输出流运算符),作用在cout上可实现屏幕输出,基本格式为:

cout<<表达式1<<表达式2<<… 其中,表达式可以是变量,常量,以及各种运算符连接的运算表达式等

每个表达式本身返回一个cout对象,因此可以将多个表达式连接起来依次输出,如:

cout<<“enter two numbers”<< endl;等效于(cout<<“enter two numbers”)<< endl;等效于cout<<“enter two numbers”;cout<< endl;

'>>'是与定义的提取符(输入流运算符),作用在cin上可实现屏幕输出,基本格式为:

cin>>表达式1>>表达式2>>… 将输入数据放到表达式里,因此表达式必须要具有存储空间(变量或内存区)

对于’>>’,如果需要连续输入多个数据,可以有两种方法:(其实跟scanf差不多,关键是缓冲区机制)

  • 每行输入一个数据,以换行符结束,如:

int a,b;cin>>a>>b; 输入:a (换行) b

  • 一行输入多个数据,数据间用空格符隔开,以换行符结束一行数据输入,如:

int a;char buf[32];cin>>a>>b; 输入:5 cheng (换行)

c++动态内存

c中使用动态内存时必须指定分配空间大小,有时很不方便
c中使用new和delete实现动态内存的分配和回收,new和delete既是C中的关键字也是一种特殊的运算符,使用时的一般格式为:

typename *p;p=new type;delete p;

编译器会自动计算type类型大小,无需指定字节数
new分配分配内存空间时可以直接对数据进行初始化,初始值可以是常量、变量以及表达式,如:

new int(100),此时该动态内存空间内的值就是100

如果初始化数据与指定数据类型不符,会对数据进行强制类型转换,如:

new int(98.55),此时存储数据为98(double转int)

使用过后,可以用delete释放p所指向的动态内存,用delete释放的空间必须是new分配的空间(堆内存),否则可能发生严重的错误

new,delete关键字可以分配数组空间,一般格式为:

typename *p=new type[n];delete[] p;

注意delete时指针前要加[],表示删除数组空间而不是p指向的数组首地址(type指针常量)所代表的type类型空间

c++动态内存示例程序:分配n个整型空间,用随机数初始化其中的数据,输出最大值和最小值

#include<iostream>
#include<stdlib.h>
#include<time.h>
int main()
{
    using namespace std;
    int *p,i,j,tmp,n;
    srand((float)time(0));
    cin>>n;
    p=new int[n];
    for(i=0;i<n;i++)
    {
        p[i]=rand()%(n-1);
    }
    for(i=1;i<n;i++)
    {
        for(j=0;j<n-i;j++)
        {
            if(p[j]>p[j+1])
            {
                tmp=p[j];
                p[j]=p[j+1];
                p[j+1]=tmp;
            }
        }
    }
    cout<<"max="<<p[0]<<" "<<"min="<<p[n-1];
    delete p;
}