C++ Language
CentOS系统下
安装C++的编译器g++:yum install gcc-c++
使用g++也可以编译之前的C文件,编译的命令和gcc类似,只是将gcc改为了g++。
输入输出:在c中我们使用scanf和printf来进行输入输出的,这个语法在C++中仍然适用。g++中也增加了新的cin和cout库来进行输入输出。
C++中新增一个bool类型:取值true和false,占用的字节大小是1。如:bool b;b = true;if(b){printf("this is true\n");}
C++中适用for循环的时候可以将变量直接定义在语句中:for(int i =0;i<10;i++){}。在以前C中必须将i变量定义在外边:int i;for(i =0;i<10;i++){}
C++缺省参数:在C++定义函数的时候如果有多个参数可以设置缺省参数,给参数设置一个默认值,如果调用函数的时候没有传入设置的缺省参数则适用默认值。如:
void func(inti val=1);
int main()
{
func();
}
void func(int val)
{
printf("val=%d\n",val);
}
注意:缺省参数只能放在非缺省参数后面
C++动态内存分配:在C中使用malloc()和free()函数进行动态内存的分配和释放,在C++中仍然可用,也提供了两个新的关键字new和delete来分配和释放内存。
C++的main函数只能以int做为返回值,不能设置为void。
C中不允许同名的函数存在,在C++中可以有函数的重载,相同名称的函数是允许的只要参数不同(包括参数个数,参数类型和参数顺序)就可以。返回值不能作为函数重载的依据。
C++中类的使用,在C中没有类只有结构体。C++不仅有结构体还有类的概念,下面是类的简单使用:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
class Student
{
public:
Student();//构造函数
~Student();//析构函数
char name[50];
int age;
double height;
int show();
};
int main()
{
Student stu;
strcpy(stu.name,"大海森");
stu.age=18;
stu.height=1.78;
stu.show();
}
int Student::show()//这里是定义一个类的方法
{
printf("姓名:%s,年龄:%d,身高:%lf\n",name,age,height);
}
同样也可以用类来定义指针,如:Student *stuptr stu;stuptr=&stu;strcpy(stu->name,"大海森");stu->age=18;
在定义类的时候不能给类的属性赋上初始值,因为在C++中类只是作为一个类型的模板使用。
类的成员定义的时候同样有访问修饰符public、private、ptotected。默认是private。
构造函数与类同名,没有返回类型,可以重载,必须是public,不能被显示的调用,只有在初始化类的时候程序自动调用,一般用于初始化操作,如在上面的类的构造函数中初始化:
Student::Student()
{
memset(name,0,sizeof(name));
age = 0;
height=0;
}
析构函数命名在类名前面加上~,析构函数在对象销毁时会自动调用,也可以手动调用,析构函数一般做一些释放的工作:
Student::~Student()
{
printf("析构函数调用了!");
}
C++中的引用:&在C中是取地址符可以用于获取一个变量的地址,在C++中&除了取地址符的用法外还可以用于定义引用。int a =10;int &i =a;,作用相当于给变量a取了一个别名i,在操作i变量的时候也等同于操作a变量,他们指向同一个地址。数组类型的变量不能定义引用,因为数组中包含若干个元素。引用必须在定义的时候就赋值,不能只定义不赋值如:int &i;会报错。
引用在C++中主要用于函数参数的传递,在C中我们通过向函数传入地址的方式,然后再函数中改变传入地址中存的值就实现了改变函数外面该变量的值的效果,如:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
void func(int *a);
void func(int &a);
int main()
{
int i =20;
func(&i);
printf("i=%d\n",i);
int ii = 20;
func(ii);
printf("ii=%d\n",ii);
}
//C中采用传入地址的方式
void func(int *a)
{
*a = 30;
}
//C++中可以采用传入引用的方式
void func(int &a)//因为传入引用后相当于再函数中执行了这样一句话:int &a = ii;即利用引用为ii起了一个别名
{
a=30;
}
C++中的string类:再C中字符串只能用以0结尾的字符数组表示,在C++中提供了一个string类来操作字符串。要使用这个string类需要包含头文件#include <string>还要引入std命名空间:using namespace stf;,如果不引入std命令空间那么string类型就要表示为std::string。然后就可以使用string str = "test";这种方式直接声明一个字符串了。
#include <stdio.h>
#include <string.h>
#include <string>
using namespace std;
int main()
{
string str;
char strname[30];
strcpy(strname,"Tom");
str = strname;//字符数组的赋值方式
printf("%s\n",str.c_str());
str = "Jack";
printf("%s\n",str.c_str());
if(str=="Jack")
{
printf("判断等号使用\n");
}
string str1 = " love me";
string str2 = str+str1;
printf("%s\n",str2.c_str());
//string类型可以用下标获取其中的字符
char c = str2[0];
printf("第一个字符是:%c\n",c);
printf("字符串的大小是%d\n",str2.size());
printf("字符串的长度是%d\n",str2.length());
}
string类中还有很多其他的函数比如查找,判断是否为空等。
C++中的vector容器:vector容器是为了改善C中的数组使用起来比较麻烦的问题。因为数组是定长的,插入和删除也比较麻烦。
#include <stdio.h>
#include <vector>
#include <string>
#include <algorithm>//sort函数需要的头文件
using namespace std;
int main()
{
vector<string> names;//声明一个string类型的vecor容器
names.push_back("Tom");//向容器尾部添加一个元素
printf("%s\n",names[0].c_str());//获取vector容器中的函数
vector<int> ages;
ages.push_back(8);
ages.push_back(2);
ages.push_back(5);
ages.push_back(1);
ages.push_back(4);
printf("排序前:");
for(int i =0;i<ages.size();i++)
{
printf("%d ",ages[i]);
}
printf("\n");
sort(ages.begin(),ages.end());//对容器中的数据进行排序,begin()返回容器头的指针指向第一个元素。end()指向最后一个元素的下一个位置
printf("排序后:");
for(int i =0;i<ages.size();i++)//用size()获取vector的大小
{
printf("%d ",ages[i]);
}
printf("\n");
}
vector也还有很多的成员函数,需要的时候自己去网上查找
C++中用new和delete内存管理:datetype *pointer = new datatype;,datatype可以是C++中的任意类型包括结构体和类,pointer是一个指针,如:
int *pi = new int;
(*pi)=10;
delete pi;
如果datatype是类,用new的时候相当于创建对象会调用构造函数,delete会调用析构函数。
C++中的运算符重载:在C++中运算符一般只能用于基本的数据类型,在类中可以对运算符进行重载,从而赋予运算符用于类的对象时有自己的含义,比如用==来判断两个对象是否相等,如果对象中的name属性值都相等为相等:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <string>
using namespace std;
class Student
{
public:
~Student();
string name;
int age;
double height;
int show();
bool operator==(const Student &student)//重载==符号,如果name值相等两个对象就相等
{
if(name == student.name)
{
return true;
}
}
};
int main()
{
Student stu;
stu.name="大海森";
stu.age=18;
stu.height=1.78;
stu.show();
Student stu1;
stu1.name="大海森";
if(stu==stu1)
{
printf("两个对象相等\n");
}
}
Student::~Student()
{
printf("析构函数调用了\n");
}
int Student::show()
{
printf("姓名:%s,年龄:%d,身高:%lf\n",name.c_str(),age,height);
}
类的继承:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <string>
using namespace std;
class Student
{
public:
~Student();
string name;
int age;
double height;
int show();
};
class SchoolTeamer:public Student//继承自Student类,public表示尽行public类型的继承,自然还有private和protec
ted类型的继承,但是我们基本用不到,有需要的时候可以去查询具体的使用效果
{
public:
string sport;
};
int main()
{
SchoolTeamer stu;
stu.name="大海森";
stu.age=18;
stu.height=1.78;
stu.sport="basketball";
stu.show();
}
Student::~Student()
{
printf("析构函数调用了\n");
}
int Student::show()
{
printf("姓名:%s,年龄:%d,身高:%lf\n",name.c_str(),age,height);
}
当一个类public继承一个基类时,基类所有的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能被派生类直接访问。
基类的指针可以指向派生类的对象,但是不能访问派生类中特有的成员。如上面的两个类有:Studen *stuptr;SchoolTeamer stu;stuptr=&stu;,用stuptr->name是可以的,但是stuptr->sport是不行的因为sport是派生类中特有的成员基类中没有。
派生类的指针不可以指向基类的指针。
一个派生类可以继承多个基类,多个基类之间用逗号分隔开。
类的多态:如果基类和子类都声明了一个相同的方法show,那么基类的对象和字类的对象在调用show()方法时调用的就是自身类中的show方法,同样基类的指针和字类的指针也遵循这个规则。
但是如果在父类的show方法定义前加上virtual关键字后,父类通过指针来调用show方法的时候都会去调用子类的show方法。这种加上virtual关键字的方法称为虚方法。
当父类的show方法被定义为虚方法,然后子类中没有自己的show方法的时候,当字类调用show方法时就是去调用的父类的show方法。
父类的虚函数可以定义为这种:virtual int show()=0;,这中方式定义的虚函数叫做纯虚函数,在父类中没有对show函数做实现,所以继承了父类的字类要求必须实现这个函数,不然就会报错。
如果一个类中定义了纯虚函数,那么不能用这个类来创建对象,这种不能用于实例化的类称作接口。
命名空间的使用:在C++中我们的main函数里面可以定义很多全局变量,我们也会引用很多外部的文件,引用的外部文件中定义的全局变量也会被引入到我们的mian函数中,如果项目很大的话那么我们的全局中定义的变量就会变得很多,而变量名的定义不允许重复,所以就会产生问题,命名空间可以用于缓解这个问题。
如下定义一个自己的头文件,用namespace包裹里面的成员。
定义一个_studentclass.h头文件:
#ifndef _STUDENTCLASS_H
#define _STUDENTCLASS_H
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <string>
namespace myspace{
class Student
{
public:
std::string name;
int age;
double height;
int show();
};
}
#endif
定义头文件的实现文件_studentclass.cpp
#include "_studentclass.h"
namespace myspace{
int Student::show()
{
printf("test\n");
}
}
引用上面定义的头文件,实现从namespace中引用定义的类:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <string>
#include "./_studentclass.h"
int main()
{
myspace::Student stu;
stu.name="大海森";
stu.age=18;
stu.height=1.78;
stu.show();
}
编译的语句:g++ -o namespacetest namespacetest.cpp _studentclass.cpp
存储类和变量作用域以及链接的理解:实际上我们再C++中声明的所有变量都有一个对应的存储类,它决定了该变量的值存储在计算机的什么地方、如何存储、以及变量的作用域。
默认的存储类是auto,我们很少看见这个关键字,但是我们定义一个变量的时候默认就是aoto的,这种变量存储在成为栈的临时内存中,它有着最小的作用域,当程序执行到语句块或者函数末尾的右花括号的时候,他们将被系统回收,不复存在。
我们也可以将变量声明为static,它被存储在静态存储区,在程序的生命周期类将一直保持它的值,直到程序退出,和全局变量的作用时间类似。一个static变量可以有external和internal两种链接方式。
还有一个存储类是extern,它在有多个翻译单元时非常重要(翻译单元的意思就是一个cpp文件编译出来的一个文件),这个关键字用来将另一个翻译单元中的某个变量声明为本翻译单元里的一个全局同名变量,编译器不会为extern类型的变量分配内存,因为它已经在其他地方分配了。
register存储类,它要求编译器将变量存储在cpu的寄存器里面,这样变量的读取将会非常快速,它与auto类型的变量有着相同的作用域。
链接:在C++的代码运行一般也是下面几个步骤:1.代码的预处理,比如将头文件的内容添加到.cpp文件里面,好像这个头文件的代码原本就在.cpp文件中一样 2.将预处理后的.cpp文件编译为对应的二进制文件 3.如果只有一个源文件(即编译的时候只编译了一个.cpp的文件),这里就只是添加一些标准库代码然后生成一个可执行文件。但如果编译多个.cpp文件,在步骤2中进行每个文件的编译过后还需要将他们按照一定的方式链接在一起才能生成最终的可执行文件。
链接一般有三种类型:外连接(external)、内链接(internal)、无链接(none)。
外链接的意思是每个翻译单元都可以访问的东西(前提是知道有这个东西存在),普通的类、函数、变量、模板和命名空间都是外链接。比如我们在编译的时候同时编译了a.cpp和b.cpp文件,那么 在a.cpp文件中定义的类、函数和全局变量和命名空间在b.cpp中就可以直接访问使用。但是并不是在a.cpp文件中定义了int i =2;在b.cpp中就可以直接访问i,除非我们有用一个头文件将他们链接到一块,即在b.cpp中include一个头文件在该头文件中定义int i,就像我们一直使用的那样。但是如果不用头文件我们可以在b.cpp中使用extern关键字来访问a.cpp中定义的i变量:extern int i ; int a = i;。
内链接的意思就是在某个翻译单元中定义的就只能在该翻译单元中使用,比如任何函数以外定义的静态变量都是内链接。
在函数中定义的变量,只能存在于该函数内部,他们没有任何链接,所以是无链接。
在C++中也有泛型和C#中的泛型使用方式类似一般都是用T来表示泛型,具体的使用在需要使用的时候自行查找学习。