C++语言基础
一、C++文件的构成
1、头文件
在创建MFC应用时,类向导会为每个头文件添加宏定义,例如:
#if !defined(AFX_ODBCVIEW_H__B82AC4A2_3DBE_4A29_A549_F9939BE498E3__INCLUDED_)
#define AFX_ODBCVIEW_H__B82AC4A2_3DBE_4A29_A549_F9939BE498E3__INCLUDED_
宏定义的目的是防止头文件被重复引用,当编译器编译头文件时,判断当前宏是否被定义,若没定义则进行定义,并编译头文件,否则略过当前头文件
a、引用头文件
使用<>格式引用系统的头文件,例如#include
使用""格式引用自定义头文件,例如#include "student.h"
b、头文件中只提供声明信息
C++中允许使用内联函数,将函数的声明和实现放在一起,但是,这样做不容易形成一套风格,建议在头文件中只提供声明信息,在源文件中提供实现信息,使程序的逻辑结构更加清晰
2、源文件
以.cpp为扩展名,源文件中通常包含3部分内容,即源文件版权,版本的声明,对头文件的引用,以及系统功能的实现代码
二、C++的基本要素
1、标识符
在C++语言中,变量、常量、函数、标签和用户定义的各种对象,被称之为标识符
标识符由一个或多个字符组成,标识符可以是字母、数字或下划线,但是标识符的首字母必须是字母或下划线,而不能是数字
标识符是区分大小写的,且不能与C++中的关键字同名
标识符的长度是任意的,但前1024个字符是有意义的
a、不同类型的对象使用类型作为标识符的前缀
例如:我们定义了一个整型变量,为了表示变量的类型,使用小写字符n做为前缀
int nAge;//年龄
int nKindCout;//种类数量
b、成员变量使用m_作为前缀
在定义类的成员变量时,成员变量以m_作为前缀,例如:
m_nAge;
m_nSize;
c、类名以C作为前缀
例如:class CStudent;
class CPerson;
d、全局对象以g_作为前缀
例如:g_Connection;
g_Recordset;
e、接口以I为前缀
例如:IUnknown;
IInterface;
2、关键字
关键字是C++编译器内置的有特殊意义的标识符,用户不能定义与关键字相同的标识符,在VC中,关键字默认都是以蓝色显示的
3、常量
常量,顾名思义,其值在运行时是不能改变的,但是在定义常量时可以设置初始值,在C++中可以使用const来定义一个常量,例如:const int nMaxValue=1100;
对于常量,编译器会将其放置了一个只读的内存区域,其值不能被修改,但是可以应用在各种表达式中,如果用户试图修改常量,编译器将提示错误
4、变量
其值可以改变的量叫变量,变量提供了一个具有名称(变量名)的存储区域,使得开发人员可以听过名称来对存储区域进行读写。每一个变量,都具有两个属性,也就通常所说的左值和右值,所谓左值,是指变量的地址值,即存储变量值的内存地址,右值是指变量的数据值,即内存地址中存储的数据。
例如:int a,b,c;
变量初始化,即为其设置初始值
例如:int a=10;
5、变量的存储类型
a、extern存储类型
在此之前,先搞明白,变量的声明和定义
变量的声明:是告知编译器变量的名称和数据类型
变量的定义:将为变量分配存储区域
使用extern关键字定义的变量:表示该变量只声明而不定义。
例如:extern int var;
好处:在一个应用程序包含多个文件时,如果在一个文件中定义一个全局变量,若其他文件中要访问该全局变量,则在另一个文件中通过使用关键字extern声明该全局变量,那么在该文件中就可以访问该全局变量了。
例如:一个文件中定义一个整型的全局变量var; int var=0;
而在另一个文件中通过extern关键字声明全局变量var,在该文件中就可以访问全局变量var了。extern int var;
b、static存储类型
在此之前,先明白,局部变量和全局变量
局部变量:是指在函数内定义的变量,在函数调用结束后,局部变量将消失
全局变量:是指在函数体外定义的变量,它的声明周期随着程序的结束而结束
static存储类型主要是针对局部变量的,如果定义一个静态的局部变量,它的声明周期在函数调用结束后依然存在,下次调用函数时,会依然保存上一次函数调用之后的值,并且,对于静态变量只执行一次初始化
static存储类型表示变量在函数或文件内是“持久性”变量,通常也被称为静态变量。
全局静态变量:
使用static在函数外部定义的变量,它的作用域仅限于当前定义的文件,不能够被其他文件使用extern关键字访问,可以认为静态全局变量为半个全局变量(不能够被其他文件共享)
局部静态变量:
使用static在函数内部定义的变量,当函数调用结束时,变量并不被释放,依然保留其值,当下一次调用函数时,将应用之前的变量值,它的作用域时当前函数,它不能被外部函数或文件访问。
c、register存储类型
使用register关键字定义的变量,表示变量将被放置在CPU的寄存器中。访问的时候比普通的变量快,register变量只能用于局部变量或作为函数的形式参数、而不能够定义全局的register,例如:
register int slocal=10;
对于寄存器变量,程序中不能获得变量的地址,也不能够称为全局变量和静态变量。但可以作为函数的形式参数
d、auto存储类型
变量的存储方式主要有两种,静态存储和动态存储。
全局变量、静态变量均属于静态存储,而普通的局部变量属于动态存储
auto关键字表示变量被动态存储,默认情况下的局部变量均属于auto变量(也称为自动变量),定义一个全局的auto变量是非法的,因为全局变量属于静态存储,与auto变量相互冲突
注意:在定义变量时,不能同时使用register、extern、static和auto4个关键字中的任意两个关键字。
三、数据类型
1、简单的数据类型
基本数据类型
字符型
单字符型 char
宽字符型 wchar_t
整型 int
实型
单精度 float
双精度 double
逻辑型 bool
空值型 void
构 造 类 型
派生类型
指针 type *
引用 type &
复合类型
数组 type[]
结构体 struct
共用体 union
枚 举 enum
类 class
2、布尔类型
在逻辑判断中只存在两个值,真和假,布尔型可以按整型对的,1表示真,0表示假。在程序中可以将布尔型对象赋给整型对象。例如:
bool bRet=true;//定义布尔类型的对象
int nData= bRet;//定义整型变量,将其初始化为bRet,即1;
反之,也可以将整数类型对象赋值给布尔类型的对象。
3、数组类型
数组描述的是一组相同数据类型元素的集合。在内存里,数组是连续存储的,数组按维数划分:可以分为一维、二维、多维数组。
a、一维数组的定义格式:
int nArray[5];
程序中可以通过元素在数组中的位置来访问,这被称之为索引访问或下标访问,例如:要访问上面数组中的第二个元素 nArray[1]=10;
C++中数组的下标是从0开始的,以数组nArray为例,其下标范围是0~4
在定义数组时,数组维数指定了数组中包含的元素数量,也就是说数组长度,数组维数必须大于等于1,并且必须输一个常量表达式,即在编译时就能够计算出它的值。
例如:const int nSize=5;//定义一个整型常量
int nArray[nSize];//合法的数组定义
如果去掉“const”则是非法的,因为维数是常量表达式,而变量nSize只能在程序执行到其定义时才能确定大小
数组初始化,例如:
int nArray[5]={4,6,6,7,5};//数组定义并初始化
可以对部分元素进行初始化,例如:int nArray[5]={4,6,6,7};没有被初始化的部分默认为0 ;
如果需要将数组中所有元素初始化为0,可以简写如下:int nArray[5]={0};
b、二维数组定义格式:数组类型 数组名[常量表达式][常量表达式];
在二维数组中,第一维称为行维,第二维称为列维
二维数组初始化,例如:
int nArray[2][3]={{2,3,5},{5,6,7}};
在定义二维数组时,如果需要提供全部元素的初始值,可以省略第一维的长度,但不能省掉第二维的长度,例如:int nArray[2][3]={5,6,7,4,5,3};
4、枚举类型
在开发应用程序时,经常需要使用一组标记来描述对象的状态,例如一个记录集对象可以有打开状态、编辑状态和关闭状态,在程序中为了描述记录集的状态,通常定义一组常量,例如:
const int RS_OPEN=1;
const int RS_EDIT=2;
const int RS_CLOSE=3;
如果需要编写一个函数根据参数描述的状态执行相关操作,可以编写一个类似下面的函数
void OperateRecordeset(int nSize)
{
if(nSize==RS_OPEN){//执行打开操作}
else if(nSize==RS_EDIT){//执行编辑操作}
else if(nSize==RS_CLOSE){//执行关闭操作}
}
枚举类型能够很好的解决“编译器并不能限制用户提供的没有意义的参数”,它能够将一组枚举常量与一个枚举类型名称关联,如果将函数的参数定义为某一枚举类型,则只允许该枚举常量作为函数的实际参数,在C++中使用关键字enum定义一个枚举类型,例如:
enum RecordsetState {RS_OPEN,RS_EDIT,RS_CLOSE};//定义枚举类型
在定义枚举类型时。可以为各个枚举常量提供一个整数值,如果没有提供整数值,默认第一个常量为0,第二个常量为1,以此类推。
例如:枚举类型作为函数参数
#include "stdafx.h"
#include
//RS_EDIT的值默认是 RS_OPEN+1,即4
enum RecordsetState{RS_OPEN=3,RS_EDIT,RS_CLOSE=6};
void OperateRecordset(RecordsetState nState)
{
if(nState==RS_OPEN)
{
cout<<"打开记录集"<
}
else if(nState==RS_EDIT)
{
cout<<"编辑记录集"<
}
else if(nState==RS_CLOSE)
{
cout<<"关闭记录集"<
}
}
int main(int argc,char* argv[])
{
OperateRecordset(RS_OPEN);//调用函数OperateRecordset
OperateRecordset(RS_EDIT);//调用函数OperateRecordset
OperateRecordset(RS_CLOSE);//调用函数OperateRecordset
return 0;
}
5、结构体类型
结构体是一组变量的集合,它能够将不同的数据类型的对象组合成一个整体,以描述一个新的对象,例如:
const int CHAR_LEN=128;
struct Student
{
char szName[CHAR_LEN];
int nAge;
char szSex[CHAR_LEN];
char szAddress[CHAR_LEN];
} ;
其中:struct是关键字,Student是结构体类型名称,它可以是任意合法的标识符,在花括号内定义的变量是结构体Student的成员,有时也被称之为字段(Field)或元素(Element)。在结构体定义的结尾处是一个分号,且分号不可以省略。
在C++中定义结构体变量的格式与定义普通变量的格式相同,例如:Student stu;
在定义结构体变量后,若要访问结构体中的各个成员,可以使用“.”符号来访问结构体中的成员,例如:stu.nAge=22;
6、公用体类型
公用体类型提供了一种机制,使得多个变量(公用体中的成员)可以共享同一个内存地址
例如:下面的代码定义了一种公用体类型 unType
union unType
{
char cdate;
int ndata;
};
定义共用体与定义结构体类似,只是关键字不同,公用体使用union关键字,在公用体unType中,成员cdata与ndata的起始位置相同。
由于共用体成员公用内存空间,因此如果试图改变了一个共用体成员的值,其他成员的值也会发生改变,但对于共用体来说,通常一次只需要使用一个成员,当第一一个共用体变量时,编译器会根据共用体成员中占用最多的内存空间的变量分配空间。这样使得共用体中所有成员都能获得足够的空间。
注意:共用体变量同样需要声明后才能用,而且不能直接引用共用体变量,只能引用共用体变量中的成员
7、指针类型
在应用程序中,指针非常强大,它能够在调用函数中修改函数参数,能够进行动态分配,同时也是非常危险的。
所谓的指针,实际上是一个变量,它能够包含某一个内存地址。指针能够存储变量的地址,那么通过指针自然可以访问到变量地址空间中的数据,即变量的值。
为了防止歧义,我们将指针值描述为指针指向的地址,将指针指向地址中的数据(它指向的变量值)
称之为指针指向的数据
a、定义指针
格式:数据类型 *指针变量名;
例如:int *pLen;
在定义了一个指针之后,如果指针没有进行初始化,还不能够使用指针,在使用指针之前,必须为指针赋值,因为使用一个未初始化或未赋值的指针是非常危险的。由于指针的值要求的是一个变量的地址,因此需要将一个变量的地址赋给指针对象。可以使用取地址运算符“&”获取变量的地址。下面的语句将一个整型变量的地址赋值给指针变量。
int nLen=8; int *pLen; nLen=pLen;
与定义普通变量类似,在定义指针变量时也可以直接进行初始化。例如:
int nLen=8; int *pLen=&pLen;
在指针被赋值或初始化之后,就可以利用指针来访问或修改其他变量了。为了访问指针指向的数据,需要在指针前使用“*”符号。例如:
int nLen=8; int *pLen=&pLen; cout<<"pLen指向的数据"<<*pLen<
b、指针变量的赋值
两个整型变量之间可以进行赋值,两个指针变量之间也可以进行赋值
int nLen=10;
int nCount=100;
int *pLen=&nLen;//定义一个指针,初始化为nLen的地址
int *pCount=&Count;//定义一个指针,初始化为nCount的地址
pLen=pCount;//将pCount赋给pLen,此时pLen的值和pCount的值是相同的,即变量nCount的地址,那么pLen所指向的数据(*pLen)为100;即nCount的值
c、指针与数组
使用指针不仅可以指向变量,还可以指向数据对象,对于数组来说,数组名表示的是数组的首地址,即数组中第一个元素的地址,因此将数组直接赋给指针对象是完全合法的。例如:
int nArray[5]={1,2,3,4,5};//定义一个包含5个元素的整型数据,并进行初始化
int *pIndex=nArray;//定义一个整型指针,将其初始化为一个数组对象
这样,指针pIndex就指向了数组的首地址,即数组中的第一个元素的地址。
对于指针变量来说,进行“++”运算并不是简单的对指针值自加或者对指针指向的数据自加1,而是对指针的值自加“sizeof(指针类型)”
例如:使用指针变量遍历数组
int nArray[5]={1,2,3,4,5};//定义一个包含5个元素的整型数据,并进行初始化
int *pIndex=nArray;//定义一个整型指针,将其初始化为一个数组对象
for(int i=0;i<5;i++)
{
cout<<*pIndex;//读取数组元素值
pIndex++;//使指针指向下一个数组元素
}
结果是:1 2 3 4 5
可以将花括号里的两句合并成一句
cout<<*pIndex++<
其作用是相同的。
d、指针数组
数组时相同数据类型元素的集合,那么数组中的元素可以是指针类型。此时的数组称之为指针数组。例如:int *pArray[3];//定义一个指针数组,数组中包含3个元素
数组中的每一个元素均是指针,即pArray[0],pArray[1],pArray[2]均指一个指针
为指针数组赋值,例如:
int *pArray[3];//定义一个指针数组,数组中包含3个元素
int nArray[3]={1,2,3};//定义一个数组,包含3个元素
for(int i=0;i<3;i++)
{
pArray[i]=*nArray[i];//为指针数组元素赋值
cout<<*pArray[i]<
}
C++中定义一个指针的指针,可以采用如下形式:数据类型 ** 变量名; 例如:int **pIndex;
这样int *pArray[3]; int **pIndex=pArray;就是合法的。
为了访问pArray数组中的元素,可以使用“*”运算符,即*pIndex,但是pArray数组中的元素时指针类型。为了访问数组元素 指向的数据,需要使用**pIndex来获取。
e、常量指针和指针常量
在定义指针是,也可以使用关键字,例如:
int nVar =10;//定义一个整型变量
const int * pVar=&nVar;//定义一个整型常量的指针,并进行初始化
对于指针pVar来说,用户不能修改pVar指向的值,但是可以修改pVar指向的指针。例如:
int nVar=10;
const int *pVar=&nVar;
//*pVar=20;//错误代码,不能修改指针常量的数据
int nNum=20;
pVar=&nNum;//修改指针常量指向的地址
在定义常量指针时,也可以将const关键字放置在数据类型之后,*符号之前的位置
int const *pVar=&nVar;
在使用const关键字定义指针时,也可以将const关键字放置在指针变量的前面。但是指针变量的性质发生了改变。如下代码:
int nVar=10;
int * const pVar=&nVar;
*pVar=20;//修改指针指向的数据
int nNum=5;
//pVar=&nNum;//代码错误,不能修改常量指针指向的地址
上面的代码,定义了一个常量指针pVar,使得用户不能够修改pVar指向的地址,但是可以修改pVar指向的数据。
在定义指针的时候也可以同时使用两个const关键字,例如:
int nVar=10;
const int *const pVar=*nVar;//定义一个常量指针常量
在上面的代码中,用户即不能修改常量指针pVar,也不能修改pVar指向的地址。
8、引用类型
引用时一个已存在对象的别名,一旦使用一个对象初始化引用对象以后,那么引用对象就成了目标对象的代名词,对引用对象操作,实际就是对目标对象进行操作。即使对引用对象使用取地址运算符“&”进行取地址运算,它将获取的目标对象的地址
格式:数据类型 &引用别名=目标对象;
例如:下面定义一个整型变量,然后定义一个引用对象,将其初始化为整型变量
int nKinds=100;
int &nRefKinds=nKinds;//定义一个引用对象,将其初始化为nKinds
注意:引用对象只能在初始化时设置目标对象,其后对引用对象的操作实际上都是对目标对象的操作。
可以将常量引用初始化为字面常量或面临值。
const int & nRefLen=3;//合法的语句
int const & nRefLen=3;//等同于
const int & nRefLen=3;
这样,在程序中,就不能修改引用对象nRefLen的值了,因为目标对象是个常量
由引用的性质可以推断,引用一个常量是没有意义的,但是合法的
9、自定义类型
用户可以使用typedef关键字自定义数据类型,并不是真的创建新的数据类型,而是在已存在的数据类型定义一个新的名称
格式:typedef 数据类型 新的名称;
例如:typedef unsigned int UIMT;
当定义完新的数据类型后,就可以像定义普通的数据变量一样定义新数据类型变量
例如,下面的代码定义了一个UINT类型的变量
UNIT ivar=10;
注意:在程序中使用在定义类型的好处:是能够提高程序的移植性。同一种数据类型,在不同的操作系统上,其长度或性质可能是不同的,如果程序中统一使用了自定义类型,在修改程序时,只需要修改自定义类型的基本类型就可以了、代码的其他地方不需要改动。
10、数据类型转换
a、数值类型转换
在数值计算(加减乘除等操作)过程中一些常用的数值转换规则
·如果两个操作数有一个操作的类型是long double,则另一个操作数无论何种类型,均被转换成long double
·如果两个操作数有一个操作类型是double,而一个操作数不是long double类型,则另一个操作数被转换为double类型
·如果两个操作数的类型都不是double(long double)类型。而一个操作数的类型是float,则另一个操作数被转换成float型
·如果两个操作数都是整数,编译器将所有小于int型的操作提升为int类型,然后进行进行比较
强制类型装换
格式:数据类型 (表达式) 或者 (表达式)数据类型
b、静态转换
静态转换:使用static_cast关键字来实现强制类型装换。
例如:使用
static_cast将整型转换成double型
double dbRate=static_cast(10)/3;
c、动态转换
动态转换通常用于一个类对象指针转换为另一个类对象的指针。如果源指针类型与目标指针类型不兼容,则转换的结果为NULL。程序中可以通过检测结果是否为NULL了来判断强制类型转换是否成功。使用关键字dynamic_cast进行动态转换
动态转换只能对void*(无类型指针)类型或者类对象指针进行转换,并且类中必须包含虚方法,而不能对普通的数据类型进行转换
d、常量转换
常量转换用于将const对象转换为非const对象。分析下面的语句:
const int MAX_LEN=100;
int *pLen=const_cast&MAX_LEN;//常量转换
e、重解释装换
它能够将任何指针类型转换为其他的指针类型,不安全,尽量少使用。
转换时使用的关键字是:reinterpret_cast
f、数值类型与字符串间的转换
1)、字符串转换为整数
语法:int atoi(const char* string);
其中:string表示转换后的字符串;返回值:如果成功,返回值为字符串转换后的整数。若不成功,返回0;如果参数string转换后数值溢出,返回值未知。
例如:
char *pHeight="100";
char *pWidth="20";
int nHight=atoi(pHeight);//将字符串转换为整数
int nWidth=atoi(pWidth);
int nArea=nHight+nWidth;
注意:在程序中使用atoi、atof等类型转换函数前,需要引用stdlib.h头文件。
2)、字符串转换为实数
语法:double atof(const char *string);
其中:string表示转换后的字符串;返回值:如果成功,返回值为字符串转换后的实数。若不成功,返回0.0;如果参数string转换后数值溢出,返回值未知。
char *pHeight="14.45";
char *pWidth="152.53";
double dbHight=atof(pHeight);//将字符串转换为实数
double
dbWidth=atof(pWidth);
double
dbArea=dbHight+dbWidth;
3)、整数转换为字符串
语法:char *itoa(int value,char *str,int radix);
其中:
value:表示待转换的整数
str表示一个字符指针,用于存储函数转换后的字符串
radix:表示一个基数,返回2...36;通常为10,即采用十进制转换
返回值:函数返回一个指向str参数的字符指针
例如:
int nDays=365;
char pText[128]={0};//定义一个字符数组,用于存储字符串
itoa(nDays,pText,10);//将整数转换为字符串
4)、实数转换为字符串
语法:char *fcvt(double value,int count,int *dec,int sign);
其中:
value:表示待转换的实数
count:表示小数点后的位数,即精度
dec:表示整型指针,记录小数点位置
sign:是一个整型指针,表示数字的符合,0为正数,1为负数
返回值:成功,返回一个字符指针,否则为NULL
例如:
double dbPI=3.1415926;
int nDec;
int nSign;
char *pText=fcvt(dbPI,2,&nDec,&nSign);//将dbPI转换成字符串
四、运算符
1、赋值运算符
赋值与初始化的区别
对象的初始化只能进行一次,即在定义对象时为其提供一个初始值;在使用赋值运算符时,赋值运算符左右两端的操作数数据类型必须相同且兼容。
赋值运算符有:=、+=、-=、*=、/=、%=
2、算数运算符
主要包括+、-、*、/、%
注意:在使用除法运算符时,两个整型数相除的结果也是整数,例如5/2的结果是2
3、关系运算符和逻辑运算符
关系运算符主要有:>、<、==、>=、<=、!=
逻辑运算符主要有:&&(都真才真)、!!、||(都假才假)
4、自增自减运算符
即++、--
++、--所放的位置不同,表示的意义不同,例如:
int a;
a++;(先计算再自增)
++a;(先自增再计算)
5、位运算符
位运算符主要有:&(与)、|(或)、^(异或)、~(按位取反)、<<(左移)、>>(右移)
6、sizeof运算符
sizeof以字节数为单位确定某个数据类型或对象的大小写,例如:
int nLen=100;
int nSizeObj=sizeof(nLen);//获取变量nSizeObj的大小,为4,即4个字节
int nSizeType=sizeof(int);//获取int数据类型的大小,为4,即4个字节
seziof可以作用于某个对象或数据类型上,如果作用于某个对象上,可以写为“sizeof(对象)”或者“sizeof 对象”
sizeof不仅可以测试简单对象的大小,还可以测试数组的大小、指针的长度
例如:测数组
char pText[5]="khjsk";//pText的长度是5
例如:测指针
int *pLen;
char* pText;
double *pRate;
int nIntPtr=sizeof(pLen);
//获取指针的长度4
int nCharPtr=sizeof(pText);
//获取指针的长度4
int nDoublePtr=sizeof(pRate);//获取指针的长度4
注意:在使用sizeof运算符测试指针长度时,无论指针的类型和指针所指向的数据是什么。只要在32为系统上,指针的长度都为4.因为指针存储的是对象的地址,
7、new和delete运算符
数据存储方式有两种
栈存储:存储空间小,生命周期短,局部变量和函数参数
堆存储:存储空间大,生命周期长,静态变量和全局变量
用户可以使用new运算符在堆中开辟一个空间,使变量存储在堆中,例如:
int *pData=new int;//定义一个指针,使用new在堆中开辟空间
*pData=100;//为指针在堆中的数据赋值
delete pData;//释放pSata在堆中占用的空间
运算符delete用于释放new运算符在堆中开辟的空间
new不仅可以对简单的对象分配空间,还可以为数组对象在堆中开辟空间,例如
int *pData=new int[3];//在堆中分配空间
pData[0]=2;
delete []pData;//释放堆中分配的数组空间
注意:当前指针指向了堆空间的地址,如果将其再指向其他地址,将导致无法释放堆空间
8、结合性和优先级
结合性:指表达式的整体计算方向,即从左至右或从右至左
优先级:表示的是运算符的优先执行顺序
五、表达式
1、逗号表达式
是指使用逗号运算符将两个表达式连接起来,例如:
10*5,8*9;
其运算过程是,先计算表达式1,即“10*5”;再计算表达式2,即“8*9”
2、三目表达式
即条件表达式,它由条件运算符和变量构成的,例如:
int a=4,b=5,c=3;
(a+b)>(b*c)?a:b;
若关系表达式的值为真,则整个条件表达式的结果是“:”符号前的操作,反之,是“:”符号后的操作
语句
一、语句的构成
语句由表达式和一个分号构成,例如:一个简单的语句
int nNum=200;
在编写语句时,如果只写一个分号,没有表达式的出现,这样的语句也是合法的,例如:
;
此时的语句被称为空语句
二、复合语句
复合语句也被称为块语句,是由一对大括号和一组简单语句构成,例如:
{
int a=4,b=5,c=3;
(a+b)>(b*c)?a:b;
}
注意:复合语句的结尾处是没有分号的
三、分支语句
1、if语句
if语句用于表达式的值有选择的执行语句,
格式:if(表达式){//语句}else{//语句}
注意:else字句在使用时采用就近原则,即else字句与其之前最近的if语句对应
2、switch语句
格式:
switch(表达式)
{
case 常量1:
语句;
break;
case 常量2:
语句;
break;
...
default:
语句;
}
其中,表达式必须有序类型,不能是实数或字符串类型,表达式逐一与case语句的常量匹配,如果发现有常量与表达式相互匹配,则执行当前case部分的语句,直到遇到break语句位置。或者到达switch语句的末尾。当表达式没有与之匹配的常量,将执行default部分的代码,default语句是可选的。若没有default语句,switch语句将不执行任何动作
例如:
int a;
switch(a)
{
case 1:
cout<<"1======="<
break;
case 2:
cout<<"2======="<
break;
case 3:
cout<<"3======="<
break;
case 4:
cout<<"4======="<
break;
default:
cout<<"0======="<
}
四、循环语句
1、while循环
格式:
while(表达式)
{
//语句
}
例如:
int main(int argc,char* argv[])
{
int nSum=0;
int i=1;
while(i<101)
{
nSum+=i;
i++;
}
cout<<"结果为:"<
return 0;
}
注意:在循环体中i需要递减,否则循环条件永远为true,循环会无休止的进行,成为了死循环
2、do while语句
语法:
do
{
//循环体
}while(表达式);
例如:
int main(int argc,char* argv[])
{
int nSum=0;
int i=1;
do
{
nSum+=i;
i++;
}while(i<101);
cout<<"结果为:"<
return 0;
}
3、for循环
语法:
for(变量初始赋值,循环条件,变量递增或递减)
{
//循环体
}
例如:
int main(int argc,char* argv[])
{
int nSum=0;
for(int i=1; i<101;i++)
{
nSum+=i;
}
cout<<"结果为:"<
return 0;
}
4、嵌套循环语句
指的是:循环语句中还包含循环语句
例如:9*9乘法表的打印
注意:在使用嵌套循环时,要注意变量的作用域。
五、跳转语句
1、goto语句
goto语句能够在函数内部实现无条件跳转
格式:goto 标号;
例如:
int main(int argc,char* argv[])
{
int nSum=0;
int i=1;
label:
nSum+=i;
i++;
if(i<100)
{
goto label;//转向标签
}
cout<<"结果为:"<
return 0;
}
其中:标号是用户自定义的一个标识符,以冒号结束,利用goto可以实现循环功能
注意:使用goto语句,应该注意标签的定义,其后不能紧接着出现“}”符号;goto语句不能越过复合语句外的变量定义的语句。
2、return语句
return语句用于退出当前调用的函数,当程序在一个函数中执行时,遇到return 语句将退出当前函数,返回调用该函数的地方开始执行下一条语句,
bool getPorint()
{
return false;
}
在函数内使用return语句时,注意return语句返回的类型与函数的返回值类型相同。return语句不可以返回一个常量值,但可以返回一个变量、表达式。其中,变量或表达式的结果作为函数的返回值。
在使用return语句提前结束函数时,需要注意如果代码之前在堆中分配了内存,则在return语句之前应释放内存,防止产生内存泄露、
3、exit语句
exit语句用于终止当前调用的进程,通常用于结束当前的应用程序,实际上,exit是一个退出当前调用进程的函数,它包含一个整型参数,标识进程退出代码。与return不同,return只是退出当前调用的函数,除非当前函数是应用程序的主函数,return语句才结束当前调用的进程,而exit语句直接结束当前调用进程,无论当前函数是否是应用程序主函数。
void ExitDemo(const int ret) {
if(ret==0)
exit(ret);
else
return;
}
当调用ExitDemo函数传递一个0值时将结束当前应用程序,传递一个非0值,则ExitDemo不会进行任何操作。
注意:在使用前需要引用头文件“iomanip.h”
面向对象程序设计
一、类与对象
1、类的定义
格式:
class 类名
{
//数据和方法的定义
};
类的定义包括两个部分,类头和类体,类头由class关键字和类名构成,类体由一组大括号{}和一个分号“;”构成,类体中通常定义类的数据和方法。数据:描述的是类的特性,也称为属性或数据成员;方法:实际上是类中定义的函数,也称方法,描述的是累的行为。
例如:定义一个CUser类
class CUser
{
private:
char m_Username[128];
char m_Password[128];
public:
bool Login()
{
if(strcmp(m_Username,"admi")==0 && strcmp(m_Password,"password")==0)
{
cout<<"登陆成功!"<
return true;
}
else
{
cout<<"登陆失败!"<
return false;
}
}
};
当方法的定义放置在类体外时,方法的实现部分首先是方法的返回值,然后是方法的名称和参数列表,最后是方法体。
注意:当方法的定义放置在类体外时,方法名称前需要使用类名和域限定符”::“来标记方法属于哪个类
在使用类的成员变量时,不能对成员变量进行初始化,这一点与定义普通的变量不同
2、类的成员访问
类成员主要是指类中的数据成员和方法(方法也被称之为成员函数
)
访问修饰符
public:public成员也被称为公有成员,public成员可以在程序的任何地方进行访问
private:private成员也被称为私有成员,该成员只能在该类中访问,派生类以及程序的其他地方均不能访问私有成员。如果在定义类时,没有指定访问限定符,默认为private
protect:protect成员也被称为保护成员,该成员只能在该类和该类的派生类(子类)中访问,除此之外,程序的其他地方不能访问保护成员。
注意:
a、类成员是具有访问权限的,如果类的外部访问私有或受保护的成员将出现访问错误
b、在定义类时,也可以将类对象声明为一个指针,CUser *pUser;程序中可以使用new运算符来为指针分配内存。例如:CUser *pUser=new CUser;或者 :CUser *pUser=new CUser();
c、如果类对象被定义为指针,需要使用”->“运算符来访问类的成员,而不能使用”.“运算符来访问
d、如果将类定义为常量指针的话,则对象只允许调用const方法。
3、构造函数和析构函数
每一个类都有构造函数和析构函数,其中,构造函数在定义对象时被调用,析构函数在对象释放时被调用,如果用户没有提供构造函数,系统将提供默认的构造函数和析构函数
a、构造函数
构造函数是一个与类同名的方法,可以没有参数,有一个参数或多个参数,但是构造函数没有返回值。如果够咱函数没有参数,该函数被称为类的默认构造函数。
如果用户为类定义了构造函数,无论是默认构造函数还是非默认构造函数,系统均不会提供默认的构造函数。
一个类可以包含多个构造函数,各个构造函数之间通过参数列表进行区分
如果想要定义一个CUser对象的指针,并调用非默认构造函数进行初始化,可以采用如下的格式:CUser *pUser=new CUser("admin","password");//调用非默认构造函数为指针分配空间
类的构造函数通过使用冒号”:“运算符提供了初始化成员的方法,如:CUser():m_Username("admin"),m_Password("password"){}
复制构造函数
复制构造函数与其他的构造函数类似,以类名作为函数的名称,但是参数只有一个,即该类飞常量引用类型。因为复制构造函数的目的是为函数复制时间参数,没有必要在复制构造函数中修改参数,因此参数定义为常量类型
#include
#include
class CBook
{
public:
char m_BookName[128];
const unsigned int m_Price;
int m_ChapterNum;
CBook()//默认构造函数
:m_Price(68),m_ChapterNum(56)//初始化数据
{
strcpy(m_BookName,"C++实训");
cout<<"构造函数被调用"<
}
CBook(const CBook &book)//定义复制构造函数
:m_Price(book.m_Price)//初始化数据成员
{
m_ChapterNum=book.m_ChapterNum;
strcpy(m_BookName,book.m_BookName);
cout<<"复制构造函数被调用"<
}
};
void OutputBookInfo(CBook book)
{
cout<<"BookName is "<
}
void main()
{
CBook book;
OutputBookInfo(book);
}
如果将
void OutputBookInfo(CBook book)函数中的参数改为
void OutputBookInfo(CBook &book)则赋值构造函数不会被调用,这是因为
OutputBookInfo函数是以引用类型作为参数,函数参数引用的方式传递,直接将实际参数的地址传递给函数,不涉及复制参数,因此没有调用复制构造函数
总结:编写函数时,尽量采用按引用的方式传递参数,这样可以避免调用赋值够咱函数,能够极大的提高程序的执行效率
b、析构函数
析构函数在对象超出作用范围或使用delete运算符释放对象时被调用,用于释放对象占用的空间。析构函数没有返回值,甚至void类型也不可以,析构函数也没有参数,因此析构函数是不能重载的
c、内联成员函数
在定义函数时,可以使用inline关键字将函数定义为内联函数,在定义类的成员函数时,也可以使用inline关键字将成员函数定义为内联函数。其实,对于成员函数来说,如果其定义在类体中,即使没有使用inline关键字,该成员函数被认为是内联成员函数
#include
class CUser
{
private:
char m_Username[128];
char m_Password[128];
public:
inline char* GetUsername()const;
};
char *CUser::GetUsername()const//内联成员函数
{
return (char*)m_Username;
}
5、静态类成员
如果将类成员定义为静态类成员,则允许使用类名直接访问,使用static关键字修饰
例如:
class CBook
{
public:
static unsigned int m_Price;
}
在定义静态数据成员时,通常需要在类体外部对静态数据成员进行初始化,例如:unsigned int CBook::m_Price=10;//初始化静态数据成员
对于静态数据成员来说,不仅可以通过对象访问,还可以直接使用类名访问。
例如:
CBook::m_Price;//使用类名直接访问静态成员
book.m_Price;//通过对象访问静态成员
在一个类中,静态数据成员是被所有的类对象所共享的,这就意味着无论定义多少个类对象,类的静态数据成员
只有一份,同时,如果某一个对象修改了静态数据成员,其他对象的静态数据成员(实际上是同一个静态数据成员)也将改变
a、静态数据成员可以是当前类的类型,而其他数据成员只能是当前类的指针或引用类型。
在定义类成员时,对于静态数据成员,其类型可以是当前类的类型,而非静态数据成员则不可以,除非数据成员的类型为当前类的指针或引用类型
static unsigned int m_Price;
CBook m_Book;//错误,不允许在该类中定义所属类的对象
static CBook m_VBook;//正确,静态数据成员允许定义类的所属类对象
CBook *m_pBook;//正确,允许定义类的所属类型的指针类型对象
在定义类的成员函数时,可以为成员函数指定默认参数,其参数的默认值也可以是累的静态数据成员,但普通的数据成员则不能作为成员函数的默认参数
static unsigned int m_Price;
void OutputInfo(int data=m_Price)//定义一个函数,以静态数据成员作为默认参数
void OutputPage(int page=m_Pages)//错误定义,类的普通数据成员不能作为默认参数
{
}
定义静态成员函数与定义普通的成员函数类似,只是在成员函数前加static关键字。
例如:static void OutputInfo();//定义静态成员函数
static unsigned int m_Price;
对于静态成员函数不能定义为const成员函数,即静态成员函数末尾不能使用const关键字。
在定义静态数据成员函数时,如果函数的实现代码处于类体之外,则在函数的实现部分不能再标识static关键字。
对于非静态成员,每一个对象都有自己的一份拷贝,即每个对象都有自己的数据成员和成员函数
每个类的成员函数(非静态成员函数)中都隐含着一个this指针,指向被调用的对象的指针,其类型为当前类型的指针类型,在const方法中,为当前类类型的const指针类型
#include
class CBook
{
public:
int m_Pages;
void OutputPages()
{
cout<
}
};
int main(int argc,char* argv[])
{
CBook vbook,cbook;
vbook.m_Pages=521;
cbook.m_Pages=563;
vbook.OutputPages();
cbook.OutputPages();
return 0;
}
当bbook对象调用OutputPages时,this指针指向vbook对象。在OutputPages成员函数中,用户可以显示的使用this指针访问数据成员。
void OutputPages()
{
cout<
m_Pages<
}
实际上,编译器为了实现this指针,在成员函数中添加了this指针对数据成员或方法。类似于上面的OutputPages方法
说明:
为了将this指针指向当前调用对象,并在成员函数中能够使用,在每一个成员函数中都隐含着一个this指针作为指针参数,并在函数调用时将对象自身的地址隐含作为实际参数传递
例如:
void OutputPages(CBook *this)//隐含添加this指针
{
7、运算符重载
如果我们在类中使用”+“重载运算符,则可以实现两个同类型对象的加法运算
运算符重载
#include
#include
class CUser
{
public:
char m_Username[128];
char m_Password[128];
int m_nLevel;//操作员级别
public:
CUser()//默认构造函数
{
m_nLevel=1;
strcpy(m_Username,"admin");
strcpy(m_Password,"password");
cout<<"构造函数被调用!"<
}
CUser operator+(CUser &refUser)//重载“+”运算符
{
CUser user;
user.m_nLevel=m_nLevel+refUser.m_nLevel;
return user;
}
};
上述代码中,为CUser类实现了+运算符的重载,运算符重载需要使用operator关键字,其后是需要重载的运算符,参数及返回值根据实际需要来设置,下面定义了3个CUser对象。将两个相加,赋值给第三个对象
int main(int argc,char* argv)
{
CUser User,comUser;
CUser defUser=User+comUser;
cout<<"m_nLever="<
return 0;
}
如果用户想要实现CUser对象与一个整数相加,可以通过修改重载运算符的参数来实现。例如:
CUser operator + (int nDate)//实现CUser对象与整数的加法运算
{
CUser user;
user.m_nLevel=m_nLevel+nData;
return user
}
实际上
CUser operator + (int nDate)与
CUser operator + (CUser &refUser)函数是可以同时存在的,即表示重载函数。这样在CUser类中即可以实现两个CUser类对象的加法运算。也可以实现CUser对象与整数的加法运算。
我们知道整数的加法运算符合交换律,即A+B等于B+A,但对于重载的+运算符来说,”User+10“不可以写为”10+User“,为了使语句”CUser defUser=10+User;“能够通过编译,可以定义一个全局的运算符重载函数。
例如:
CUser operator+(int nData,CUser &refUser)//全局运算符重载
{
CUser user;
user.m_nLevel=refUser.m_nLevel+nData;
return User;
}
这样
语句”CUser defUser=10+User;“就成了合法的语句。
在上述函数中,注意参数的顺序,整型要放置在前面,CUser类型放置在后面,不能够进行倒置。否则语句仍然不能通过编译
前置运算:
在默认情况下,如果重载运算符没有参数,则表示前置运算,例如:
void operator++()
{
++m_nLevel;
}
如果重载运算符使用了整数作为参数,则表示后置运算,此时的参数值可以被忽略,它只是一个标识。例如:
void operator++(int)
{
++m_nLevel;
}
我们可以通过重载=运算符将一个整数赋给一个对象,要实现将对象赋值给整数,可以使用C++提供的转换运算符,例如:装换运算符
#include
#include
class CUser
{
public:
char m_Username[128];
char m_Password[128];
int nLevel;
public:
CUser()
{
m_nLevel=1;
strcpy(m_Username,"admin");
strcpy(m_Password,"password");
cout<<"构造函数被调用"<
}
operator int()//转换运算符
{
return m_nLevel;
}
};
转换运算符由关键字operator开始,其后是转换为的数据类型。在定义转换运算符时,之一operator关键字前没有数据类型,虽然转换符实际上返回了一个转换后的值,但是不能指定返回值的数据类型
下面在main函数中定义一个CUser对象,将该对象赋值给一个整型变量
int main(int argc,char* argv)
{
CUser user;
int nData=user;//将CUser对象赋值给提个整型变量
cout<<"m_nLevel="<
return 0;
}
注意:
a、并不是所有的C++运算符都可以被重载
大多数是可以的,但是”::“、”?“、”:“、”.“运算符不能被重载
b、运算符重载的一些限制
不能构建新的运算符
不能改变原有运算符操作数的个数
不能改变原有运算符的优先级
不能改变原有运算符的结合性
不能改变原有运算符的语法结构
c、运算符重载的基本准则
一元操作数可以是不带参数的成员函数,或是带一个参数的非成员函数
二元操作数可以是不带一个参数的成员函数,或者是带2个参数的非成员函数
”=“、”[]“、”->“和”()“运算符只能定义为成员函数
”->“运算符的返回值必须是指针类型或者能够使用”->“运算符类型的对象
重载”++“和”--“运算符时,带一个int型参数,表示后置运算,不带参数表示前置运算
8、友元类和友元方法
在程序开发时,如果两个类的耦合度比较紧密,能够在一个类中访问另一个类的私有成员会带来很大方便。C++提供友元类和友元方法来实现访问其他类的私有成员,当用户希望另一个类能够访问当前类的私有成员时,可以在当前类中将另一个类作为自己的友元类,这样在另一个类中就可以访问当前类的私有成员了
例如:定义友元类
#include
#include
class CItem
{
private:
char m_Name[128];
void OutputName()
{
cout<<"m_Name="<
}
public:
friend class CList;//为CList类作为自己的友元类
void SetItemName(const char *pchData)//定义共有成员函数,设置m_Name成员
{
if(pchData != NULL)
{
strcpy(m_Name,pchData);
}
}
CItem()//构造函数,初始化数据成员m_Name
{
memset(m_Name,0,128);
}
};
class CList
{
private:
CItem m_Item;
public:
void OutputItem();//定义共有成员函数
};
void CList::OutputItem()//OutputItem函数的实现代码
{
m_Item.SetItemName("beijing");//调用CItem类的共有方法
m_Item.OutputName();//调用CItem类的私有方法
}
上述代码中,在定义CItem类时,使用friend关键字将CList类定义为CItem类的友元,这样CList类中所有的方法都可以访问CItem类中的私有成员了。
假设需要实现只允许CList类的某个成员访问CItem类的私有成员,而不允许其他成员函数访问CItem类的私有数据。这可以通过友元函数来实现、
例如:
friend void CList::OutputItem(){}
对于友元函数来说,不仅可以是类的成员函数,还可以是一个全局函数,例如:
在类体内:friend void OutputItem(CItem *pItem);
9、类的继承
继承是面向对象的主要特征之一,它使得一个类可以从现有的类中派生,而不必重新定义一个新类。例如:
#include
#include
#define MAXLEN 128
class CEmployee
{
public:
int m_ID;
char m_Name[MAXLEN];
char m_Depart[MAXLEN];
CEmployee()
{
memset(m_Name,0,MAXLEN);
memset(m_Depart,0,MAXLEN);
cout<<"员工类构造函数被调用"<
}
void OutputName()
{
cout<<"员工姓名:"<
}
};
class COperator:public CEmployee
{
public:
char m_Password[MAXLEN];
bool Login()
{
if(strcmp(m_Name,"admin")==0 && strcmp(m_Password,"password")==0)
{
cout<<"登陆成功"<
return true;
}
else
{
cout<<"登陆失败"<
return false;
}
}
};
上述代码在定义COperator类时使用了“:”运算符,表示该类派生于一个基类,public关键字表示派生类的类型为公有型,其后的CEmployee表示COperator类的基类,也就是父类。
当一个类从另一个类继承时,可以有3种派生类型,分别为公有型(public)、私有型(private)和保护型(protect)。
public型表示对于基类中的public数据成员和方法,在派生类中仍然是public。
private
型表示对于基类中的
private
数据成员和方法,在派生类中仍然是
private
。
protect
型表示对于基类中的
protect
数据成员和方法,在派生类中仍然是
protect
。
void main()
{
COperator opera;
strcpy(opera.m_Name,"admin");
strcpy(opera.m_Password,"password");
opera.Login();
opera.OutputName();
}
用户在父类中派生子类时,可能存在一种情况,即在子类中定义了一个与父类同名的方法。此时,称为子类隐藏了父类的方法。
如果子类中隐藏了父类的方法,则父类中所有同名的方法(重载方法)均被隐藏
如果用户想要访问被隐藏的父类的方法,依然需要指定父类的名称。例如:
COperator opera;
opera.CEmployee::OutputName("admin");//调用基类中被隐藏的方法、
虚函数
在定义函数时,在函数前面使用关键字virtual关键字,使用虚方法可以实现类的动态绑定,即根据对象运行时的类型来确定调用哪个类的方法。而不是根据对象定义时的类型来确定调用哪个类的方法。
#include
#include
#define MAXLEN 128
class CEmployee
{
public:
int m_ID;
char m_Name[MAXLEN];
char m_Depart[MAXLEN];
CEmployee()
{
memset(m_Name,0,MAXLEN);
memset(m_Depart,0,MAXLEN);
cout<<"员工类构造函数被调用"<
}
virtual void OutputName()
{
cout<<"员工姓名:"<
}
};
class COperator:public CEmployee
{
public:
char m_Password[MAXLEN];
void OutputName()
{
cout<<"操作员姓名:"<
}
};
上述代码中,CEpmloyee类中定义了一个虚方法OutputName,在子类COperator类中改写了OutputName方法,其中,COperator中的OutputName方法仍为虚方法,即使没有使用virtual关键字。
void main()
{
CEmployee *pWork=new COperator();
strcpy(pWork->m_Name,"admin");
pWork->OutputName();
delete pWork;
}
此时,“
pWork->OutputName();
”语句调用的是Operator类的OutputName方法、
在C++语言中,除了能够定义虚方法之外,还可以定义纯虚方法,也就是抽象方法。一个包含纯虚方法的类被称为抽象类。抽象类是不能够被实例化的,通常用于实现接口的定义。
例如:
#define MAXLEN 128;
class CEmployee
{
public:
int m_ID;
char m_Name[MAXLEN];
virtual void OutputName()=0;
};
抽象方法的定义时在虚方法的基础上在末尾添加”=0“;对于包含纯虚方法的类来说,是不能呢个够被实例化的
抽象类通常用于作为其他类的父类,从抽象类派生的子类如果是抽象类,则子类必须实现父类中所有的抽象方法
当从父类派生一个子类后,定义一个子类的对象时,它将依次调用父类的构造函数,当前类的构造函数来创建对象,在释放子类对象时,先调用的是当前类的析构函数,然后是父类的析构函数
定义一个基类类型的指针,调用子类的构造函数为其构建对象,当对象释放时,如果析构函数是虚函数,则先调用父类的析构函数,然后再调用子类的析构函数。如果析构函数不是虚函数,则只调用父类的析构函数。
C++语言允许子类从多个父类继承公有的和受保护的成员。
多继承
#include
#include
class CBird
{
public:
void FlyInSky()
{
cout<<"鸟能够在天空飞翔!"<
}
void Breath()
{
cout<<"鸟能够呼吸"<
}
};
class CFish
{
public:
void Swimming()
{
cout<<"鱼可以在水里游"<
}
void Breath()
{
cout<<"鱼能够呼吸"<
}
};
class CWater:public CBird,public CFish
{
public:
void Action()
{
cout<<"水鸟即能飞又能游"<
}
};
int main(int argc,char *argv[])
{
CWater water;
water.FlyInSky();
water.Swimming();
return 0;
}
上述代码中,CBird类与CFish类都有Breath方法,如果CWater类对象要调用Breath方法,此时,编译器将产生歧义,不知道要具体调用哪个Breath方法。为了让CWater类对象能够访问Breath方法,需要在Breath方法前具体制定类名。例如:
water.CFish::Breath();
water.CBird::Breath();
假如CBird类和CFish类均派生于同一个父类,例如CAnimal类
C++的虚继承机制
例如:
#include
#include
class CAnimal
{
public:
CAnimal()
{
cout<<"动物类被构造"<
}
void Move()
{
cout<<"动物能够移动"<
}
};
class CBird:virtual public CAnimal
{
public:
CBird()
{
cout<<"鸟类被构造"<
}
void FlyInSky()
{
cout<<"鸟能够在天空飞翔!"<
}
void Breath()
{
cout<<"鸟能够呼吸"<
}
};
class CFish:virtual public CAnimal
{
public:
CFish()
{
cout<<"鱼类被构造"<
}
void Swimming()
{
cout<<"鱼可以在水里游"<
}
void Breath()
{
cout<<"鱼能够呼吸"<
}
};
class CWater:public CBird,public CFish
{
public:
CWater()
{
cout<<"鱼鸟被构造"<
}
void Action()
{
cout<<"水鸟即能飞又能游"<
}
};
int main(int argc,char *argv[])
{
CWater water;
/*water.FlyInSky();
water.Swimming();
water.Action();
water.CFish::Breath();
water.CBird::Breath();*/
return 0;
}
通常,在定义一个对象时,先一次调用基类的构造函数,最后才是自身的构造函数,但对于虚继承来说,情况有些不同,在定义CWater类对象时,先调用基类CAnimal的构造函数,再调用CBird的构造函数,人啊后是CFish,最后是CWater构造函数。
10、类域
所谓的类域是指类的作用域。我们在定义一个类时,类体就是类的作用域,类的所有成员均处于类中。当程序中使用点运算符”.“和箭头运算符”->“访问类的成员时,编译器会根据运算符前面的类名来确定其类域,查找类成员。如果使用域运算符(::)访问类成员,编译器将根据运算符前面的类名来确定其类域。查找类成员
在定义类时,类成员的声明顺序也是很重要的,先声明的成员不能使用后声明的成员。
在类的定义中,通常对象在使用前都需要先声明,这个规则有2个例外情况,第一种情况是内联函数,对于内联函数来说,函数的声明被放置在函数定义处处理,因此,类中的所有成员对内联函数都是可见的
例如:
class CUser
{
private:
char m_Username[128];
public:
void SetLevel(int nLevel)//内联函数
{
m_nLevel=nLevel;
}
int m_nLevel;
}
上述代码即使m_nLevel成员的定义出现在SetLevel函数定义之后,但是由于SetLevel是内联函数,即在函数定义处访问类的数据成员,自然是合法的;
第二种情况出现在成员函数的默认参数,在定义类的成员函数时,可以为成员函数指定默认参数,这个默认参数不仅可以是一个常量值,还可以是类的数据成员,准确的说是类的静态数据成员,类的非静态数据成员不能成为成员函数的默认参数。
注意:如火在类中自定义了一个类型,在类域内该类型都被用来解析成员函数参数的类型名。
例如:在类的声明时定义了一个自定义类型,那么允许在方法定义时使用该类型作为参数类型。类内部的自定义类型作为成员函数参数类型。
public:
typedef unsigned int UINT;//自定义类型UINT
void CUser::SetLevel(UINT nLevel)
{}//在类的声明之外使用UINT类型
注意:在对静态变量进行初始化时,初始化的语句也被认为在类域内
例如:
public:
static int m_nLevel;//定义静态数据成员
static int GetLevel()//定义静态成员函数
{
return 1;
}
int CUser::m_nLevel=GetLevel();//利用静态成员函数进行初始化
11、局部类
类的定义也可以放置在函数中,这样的类被称为局部类
例如:
void LocalClass()
{
class CBook
{
private:
int m_Pages;
public:
void SetPages(int nPage)
{
if(m_Pages!=nPage)
m_Pages=npage;
}
void GetPages()
{
return m_Pages;
}
};
CBook book;
book.SetPages(300);
cout<
}
12、嵌套类
在C++中,允许在一个内的内部再定义一个类,这样的类被称之为嵌套类
对于内部的嵌套类来说,只允许其在外围的类域中使用,在其它类域或者作用域中是不可见的。但是可以通过使用外围的类域作为限定符来定义CNode对象。如下的定义时合法的
void main()
{
CList::CNode node;
}
单数这样做通常是不合理的,也是有限制条件的。因为既然定义了嵌套类,通常都不允许在外界访问,这违背了使用嵌套类的原则,为了防止在外界直接使用嵌套类,可以将嵌套类定义为私有成员。
二、类模板
1、类模板的定义及应用
链表:链表的功能是向末尾节点添加数据,遍历链表中的节点,在链表结束时释放所有节点
单向链表
#include
class CNode//定义一个节点类
{
public:
CNode *m_pNext;//定义一个节点指针,指向下一个节点
int m_nData;//定义节点的数据
CNode()//定义节点类的构造函数
{
m_pNext=NULL;
}
};
class CList//定义链表类
{
private:
CNode *m_pHeader;//定义头节点
int m_nNodeSum;//定义节点数量
public:
CList()//定义链表的构造函数
{
m_pHeader=NULL;
m_nNodeSum=0;
}
CNode *MoveTrail()//移动到尾节点
{
CNode* pTmp=m_pHeader;//定义一个临时节点,将其指向头节点
for(int i=1;i
{
pTmp=pTmp->m_pNext;//获取下一个节点
}
return pTmp;
}
void AddNode(CNode *pNode)
{
if(m_nNodeSum==0)
{
m_pHeader=pNode;//将节点添加到头节点中
}
else
{
CNode* pTrail=MoveTrail();//搜索尾节点
pTrail->m_pNext=pNode;//在尾节点处添加节点
}
m_nNodeSum++;//使链表节点数量加1
}
void IterateList()
{
if(m_nNodeSum>0)//判断链表是否为空
{
CNode* pTmp=m_pHeader;//定义一个临时节点,将其指向头节点
for(int i=1;i
{
pTmp=pTmp->m_pNext;//获取下一个节点
}
}
}
~CList()
{
if(m_nNodeSum>0)
{
CNode *pDelete=m_pHeader;//定义一个临时节点,指向头节点
CNode *pTmp=NULL;
for(int i=1;i
{
pTmp=pDelete->m_pNext;//获取下一个节点
delete pDelete;
pDelete=pTmp;
}
m_nNodeSum=0;
pDelete=NULL;
pTmp=NULL;
}
m_pHeader=NULL;
}
};
void main()
{
CList list;
for(int i=0;i<5;i++)
{
CNode *pNode=new CNode();
pNode->m_nData=i;
list.AddNode(pNode);
}
list.IterateList();
}
上述代码在定义链表类CList时存在最大缺陷就是链表不够灵活,其节点只能是CNode类型,为了让CList能够适应各种类型的节点。一个最简单的办法就是使用类模板,类模板的定义与函数模板类似,以关键字template开始,其后是由尖括号构成的模板参数
模板链表
#include
class CNode//定义一个节点类
{
public:
CNode *m_pNext;//定义一个节点指针,指向下一个节点
int m_nData;//定义节点的数据
CNode()//定义节点类的构造函数
{
m_pNext=NULL;
}
};
template
class CList//定义链表类
{
private:
Type *m_pHeader;//定义头节点
int m_nNodeSum;//定义节点数量
public:
CList()//定义链表的构造函数
{
m_pHeader=NULL;
m_nNodeSum=0;
}
Type *MoveTrail()//移动到尾节点
{
Type* pTmp=m_pHeader;//定义一个临时节点,将其指向头节点
for(int i=1;i
{
pTmp=pTmp->m_pNext;//获取下一个节点
}
return pTmp;
}
void AddNode(Type *pNode)
{
if(m_nNodeSum==0)
{
m_pHeader=pNode;//将节点添加到头节点中
}
else
{
Type* pTrail=MoveTrail();//搜索尾节点
pTrail->m_pNext=pNode;//在尾节点处添加节点
}
m_nNodeSum++;//使链表节点数量加1
}
void IterateList()
{
if(m_nNodeSum>0)//判断链表是否为空
{
Type* pTmp=m_pHeader;//定义一个临时节点,将其指向头节点
for(int i=1;i
{
pTmp=pTmp->m_pNext;//获取下一个节点
}
}
}
~CList()
{
if(m_nNodeSum>0)
{
Type *pDelete=m_pHeader;//定义一个临时节点,指向头节点
Type *pTmp=NULL;
for(int i=1;i
{
pTmp=pDelete->m_pNext;//获取下一个节点
delete pDelete;
pDelete=pTmp;
}
m_nNodeSum=0;
pDelete=NULL;
pTmp=NULL;
}
m_pHeader=NULL;
}
};
class CNet
{
public:
CNet* m_pNext;
int m_nData;
CNet()
{
m_pNext=NULL;
}
};
void main()
{
CList nodelist;
for(int i=0;i<5;i++)
{
CNode *pNode=new CNode();
pNode->m_nData=i;
nodelist.AddNode(pNode);
}
nodelist.IterateList();
CList netlist;
for(int j=0;j<5;j++)
{
CNet *pNode=new CNet();
pNode->m_nData=j;
netlist.AddNode(pNode);
}
netlist.IterateList();
}
模板由template关键字开始,其后是模板参数列表,由尖括号表示,模板参数列表不能为空,它可以是一个类型参数,即由关键字class或typename和一个标示符构成,也可以是非类型参数,由一个常量表达式表示,一个类模板可以有多个类型参数。例如:
template
class CList
{//.....};
注意:每一模板参数必须由class或typename标识,不能够利用class或typename关键字定义多个模板参数
模板非类型参数由一个普通的参数声明构成,并且模板类型参数和非类型参数可以混合在一起使用,例如:
template
class CList
{//.....};
注意:
a、模板参数与全局对象重名
模板参数的名字在模板声明后直到模板介绍都可以在模板中使用们如果在全局区域定义了与模板参数相同的对象,则在模板中全局对象被隐藏
b、模板参数名不能与模板中自定义类型或类的名字相同
2、定义类模板的静态数据
在类模板中用户也可以定义静态的数据成员,只是类模板中的每个实例都有自己的静态数据成员,而不是所有的类模板实例共享静态数据成员。例如:
public:
static int m_ListValue;//定义静态数据成员
类体外
int CList::m_ListValue=100;
main函数
CList nodelist;//实例话类模板
nodelist.m_ListValue=200;//设置静态数据成员
CList netlist;//实例化类模板
netlist.m_ListValue=300;//设置静态数据成员
cout<<"nodelist实例:
"<
cout<<"netList实例:"<
结果:
nodelist实例:200
netlist实例:300
从上面的例子可以发现。模板实例nodelist和netlist均有各自的静态数据成员,但是,对于同一类型的模板实例,其静态数据成员是共享的。例如:同一类型的模板实现共享静态数据成员
void main()
{
CList nodelist;//实例话类模板
nodelist.m_ListValue=200;//设置静态数据成员
CList netlist;//实例化类模板
netlist.m_ListValue=300;//设置静态数据成员
cout<<"nodelist实例:
"<
cout<<"netList实例:"<
}
结果:
nodelist实例:300
netlist实例:300
从上例可以发现。模板实例nodelist和netlist共享静态数据成员,因为他们的模板均为CNode
三、异常处理
1、异常捕获语句
为了防止程序由于意外的异常导致中断,可以使用异常捕捉语句来提前捕捉并处理异常。使得程序在产生异常时能够正常运行,在C++语言中,为了处理异常,提供了try和catch语句,try语句和catch语句实际上是一个语句块,try语句包含的是可能产生异常的代码,catch语句块包含的是处理异常的代码。例如:
捕捉异常:
void main()
{
try
{
int nDiv=100;
int nDivisor=0;
int nRet=nDiv/nDivisor;
cout<
}
catch(...)
{
cout<<"产生异常!"<
}
}
结果:常数异常!
从结果上看,当程序代码产生异常时,程序并没有中断,因为在程序代码(catch语句部分)中处理了该异常。
在catch语句中出现了...符号,表示处理所有异常,如果try语句中没有产生异常,则不会执行catch语句块中的代码,实际上一个try语句可以对应多个catch语句。每一个catch语句可以关联一个异常类,当try语句块中产生的异常与catch语句关联的异常匹配时,将执行该catch语句块中的代码。
注意:
try部分的语句是可能出现错误的语句,该语句必须由大括号包含,即使只有一条语句,catch部分的语句是处理异常的语句,该部分的语句也必须由大括号包含,在try程序段后,必须紧跟着一个活多个catch程序段,每个catch程序段用于捕捉一种类型的异常,并列的catch程序之间不允许插入其他语句。
2、抛出异常
异常不仅可以由系统自动触发(由执行的程序代码所致),也是由用户自己触发异常,自己触发异常的好处是用户可以定制自己的逻辑规则,例如:当用户的某一项操作不符合业务规则时,我们可以自定义一个异常,在该情况发生时触发异常,交由异常处理语句进行相应处理。
C++中提供了throw关键字用于触发异常,下例中,如果除数为0,则使用throw关键字抛出异常
void main()
{
try
{
int nDiv=100;
int nDivisor=0;
if(nDivisor<=0)
{
throw"除数必须大于0";//抛出异常
}
int nRet=nDiv/nDivisor;
cout<
}
catch(...)
{
cout<<"运算失败!"<
}
}
运行结果:运算失败!
自定义异常类
首先定义两个异常类
#include
#include
//除零异常类
class CDivZeroException
{
public:
char ExceptionMsg[128];
CDivZeroException()
{
strcpy(ExceptionMsg,"除零错误!");
}
};
//除数为负数异常类
class CNegException
{
public:
char ExceptionMsg[128];
CNegException()
{
strcpy(ExceptionMsg,"除数为负数错误");
}
};
//定义一个除法函数
bool Div(int nDiv,int nDivisor,int &nRet)
{
try
{
if(nDivisor==0)
{
throw CDivZeroException();
}
else if(nDivisor<0)
{
throw CNegException();
}
else
{
nRet=nDiv/nDivisor;
}
}
catch(CDivZeroException e)
{
cout<
return false;
}
catch(CNegException e)
{
cout<
return false;
}
return true;
}
int main(int argc,char *argv[])
{
int nRet;
bool bRet=Div(100,4,nRet);
你可能感兴趣的:(C++)
c++ 的iostream 和 c++的stdio的区别和联系
黄卷青灯77
c++ 算法 开发语言 iostream stdio
在C++中,iostream和C语言的stdio.h都是用于处理输入输出的库,但它们在设计、用法和功能上有许多不同。以下是两者的区别和联系:区别1.编程风格iostream(C++风格):C++标准库中的输入输出流类库,支持面向对象的输入输出操作。典型用法是cin(输入)和cout(输出),使用>操作符来处理数据。更加类型安全,支持用户自定义类型的输入输出。#includeintmain(){in
【JS】执行时长(100分) |思路参考+代码解析(C++)
l939035548
JS 算法 数据结构 c++
题目为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU不空闲情况下,最少需要多长时间执行完成。题目输入第一个参数为GPU一次最多执行的任务个数,取值范围[1,10000]第二个参数为任务数组长度,取值范围[1,10000]第三个参数为任务数组,数字范围
基于CODESYS的多轴运动控制程序框架:逻辑与运动控制分离,快速开发灵活操作
GPJnCrbBdl
python 开发语言
基于codesys开发的多轴运动控制程序框架,将逻辑与运动控制分离,将单轴控制封装成功能块,对该功能块的操作包含了所有的单轴控制(归零、点动、相对定位、绝对定位、设置当前位置、伺服模式切换等等)。程序框架由主程序按照状态调用分归零模式、手动模式、自动模式、故障模式,程序状态的跳转都已完成,只需要根据不同的工艺要求完成所需的动作即可。变量的声明、地址的规划都严格按照C++的标准定义,能帮助开发者快速
C++ | Leetcode C++题解之第409题最长回文串
Ddddddd_158
经验分享 C++ Leetcode 题解
题目:题解:classSolution{public:intlongestPalindrome(strings){unordered_mapcount;intans=0;for(charc:s)++count[c];for(autop:count){intv=p.second;ans+=v/2*2;if(v%2==1andans%2==0)++ans;}returnans;}};
C++菜鸟教程 - 从入门到精通 第二节
DreamByte
c++
一.上节课的补充(数据类型)1.前言继上节课,我们主要讲解了输入,输出和运算符,我们现在来补充一下数据类型的知识上节课遗漏了这个知识点,非常的抱歉顺便说一下,博主要上高中了,更新会慢,2-4周更新一次对了,正好赶上中秋节,小编跟大家说一句:中秋节快乐!2.int类型上节课,我们其实只用了int类型int类型,是整数类型,它们存贮的是整数,不能存小数(浮点数)定义变量的方式很简单inta;//定义一
Java面试题精选:消息队列(二)
芒果不是芒
Java面试题精选 java kafka
一、Kafka的特性1.消息持久化:消息存储在磁盘,所以消息不会丢失2.高吞吐量:可以轻松实现单机百万级别的并发3.扩展性:扩展性强,还是动态扩展4.多客户端支持:支持多种语言(Java、C、C++、GO、)5.KafkaStreams(一个天生的流处理):在双十一或者销售大屏就会用到这种流处理。使用KafkaStreams可以快速的把销售额统计出来6.安全机制:Kafka进行生产或者消费的时候会
C++ lambda闭包消除类成员变量
barbyQAQ
c++ c++ java 算法
原文链接:https://blog.csdn.net/qq_51470638/article/details/142151502一、背景在面向对象编程时,常常要添加类成员变量。然而类成员一旦多了之后,也会带来干扰。拿到一个类,一看成员变量好几十个,就问你怕不怕?二、解决思路可以借助函数式编程思想,来消除一些不必要的类成员变量。三、实例举个例子:classClassA{public:...intfu
2021 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级C++语言试题 (第三大题:完善程序 代码)
mmz1207
c++ csp
最近有一段时间没更新了,在准备CSP考试,请大家见谅。(1)有n个人围成一个圈,依次标号0到n-1。从0号开始,依次0,1,0,1...交替报数,报到一的人离开,直至圈中剩最后一个人。求最后剩下的人的编号。#includeusingnamespacestd;intf[1000010];intmain(){intn;cin>>n;inti=0,cnt=0,p=0;while(cnt#includeu
《 C++ 修炼全景指南:九 》打破编程瓶颈!掌握二叉搜索树的高效实现与技巧
Lenyiin
C++ 修炼全景指南 技术指南 c++ 算法 stl
摘要本文详细探讨了二叉搜索树(BinarySearchTree,BST)的核心概念和技术细节,包括插入、查找、删除、遍历等基本操作,并结合实际代码演示了如何实现这些功能。文章深入分析了二叉搜索树的性能优势及其时间复杂度,同时介绍了前驱、后继的查找方法等高级功能。通过自定义实现的二叉搜索树类,读者能够掌握其实际应用,此外,文章还建议进一步扩展为平衡树(如AVL树、红黑树)以优化极端情况下的性能退化。
20个新手学习c++必会的程序 输出*三角形、杨辉三角等(附代码)
X_StarX
c++ 学习 算法 大学生 开发语言 数据结构
示例1:HelloWorld#includeusingnamespacestd;intmain(){coutusingnamespacestd;intmain(){inta=5;intb=10;intsum=a+b;coutusingnamespacestd;intfactorial(intn){if(nusingnamespacestd;voidprintFibonacci(intn){intt
C++八股
Petrichorzncu
八股总结 c++ 开发语言
这里写目录标题C++内存管理C++的构造函数,复制构造函数,和析构函数深复制与浅复制:构造函数和析构函数哪个能写成虚函数,为什么?C++数据结构内存排列结构体和类占用的内存:==虚函数和虚表的原理==虚函数虚表(Vtable)虚函数和虚表的实现细节==内存泄漏==指针的工作原理函数的传值和传址new和delete与malloc和freeC++内存区域划分C++11新特性C++常见新特性==智能指针
【2022 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题及解析】
汉子萌萌哒
CCF noi 算法 数据结构 c++
一、单项选择题(共15题,每题2分,共计30分;每题有且仅有一个正确选项)1.以下哪种功能没有涉及C++语言的面向对象特性支持:()。A.C++中调用printf函数B.C++中调用用户定义的类成员函数C.C++中构造一个class或structD.C++中构造来源于同一基类的多个派生类题目解析【解析】正确答案:AC++基础知识,面向对象和类有关,类又涉及父类、子类、继承、派生等关系,printf
《 C++ 修炼全景指南:十 》自平衡的艺术:深入了解 AVL 树的核心原理与实现
Lenyiin
C++ 修炼全景指南 技术指南 c++ 数据结构 stl
摘要本文深入探讨了AVL树(自平衡二叉搜索树)的概念、特点以及实现细节。我们首先介绍了AVL树的基本原理,并详细分析了其四种旋转操作,包括左旋、右旋、左右双旋和右左双旋,阐述了它们在保持树平衡中的重要作用。接着,本文从头到尾详细描述了AVL树的插入、删除和查找操作,配合完整的代码实现和详尽的注释,使读者能够全面理解这些操作的执行过程。此外,我们还提供了AVL树的遍历方法,包括中序、前序和后序遍历,
JAVA学习笔记之23种设计模式学习
victorfreedom
Java技术 设计模式 android java 常用设计模式
博主最近买了《设计模式》这本书来学习,无奈这本书是以C++语言为基础进行说明,整个学习流程下来效率不是很高,虽然有的设计模式通俗易懂,但感觉还是没有充分的掌握了所有的设计模式。于是博主百度了一番,发现有大神写过了这方面的问题,于是博主迅速拿来学习。一、设计模式的分类总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。结构型模式,共七种:适配器
c++ opencv4.3 sift匹配
图像处理大大大大大牛啊
图像处理 opencv实战代码讲解 opencv sift c++ opencv4 特征点
c++opencv4.3sift匹配main.cppintmain(){vectorkeypoints1,keypoints2;Matimg1,img2,descriptors1,descriptors2;intnumF
《 C++ 修炼全景指南:四 》揭秘 C++ List 容器背后的实现原理,带你构建自己的双向链表
Lenyiin
技术指南 C++ 修炼全景指南 c++ list 链表 stl
本篇博客,我们将详细讲解如何从头实现一个功能齐全且强大的C++List容器,并深入到各个细节。这篇博客将包括每一步的代码实现、解释以及扩展功能的探讨,目标是让初学者也能轻松理解。一、简介1.1、背景介绍在C++中,std::list是一个基于双向链表的容器,允许高效的插入和删除操作,适用于频繁插入和删除操作的场景。与动态数组不同,list允许常数时间内的插入和删除操作,支持双向遍历。这篇文章将详细
c++ 内存处理函数
heeheeai
c++ 开发语言
在C语言的头文件中,memcpy和memmove函数都用于复制内存块,但它们在处理内存重叠方面存在关键区别:内存重叠:memcpy函数不保证在源内存和目标内存区域重叠时能够正确复制数据。如果内存区域重叠,memcpy的行为是未定义的,可能会导致数据损坏或程序崩溃。memmove函数能够安全地处理源内存和目标内存区域重叠的情况。它会确保在复制过程中不会覆盖尚未复制的数据,从而保证数据的完整性。效率:
【c++基础概念深度理解——堆和栈的区别,并实现堆溢出和栈溢出】
XWWW668899
C++基本概念 c++ c语言 开发语言 青少年编程
文章目录概要技术名词解释栈溢出和堆溢出小结概要学习C++语言,避免不了要好好理解一下堆(Heap)和栈(Stack),有助于更好地管理内存,以及如何写出一段程序“成功实现”堆溢出和栈溢出。技术名词解释理解东西最快的方式是根据自己目前能理解的词语去关联新的概念,不断的纠正,向正确的深度理解靠近,当无限接近的时候也就理解了想要理解的概念。我们经常说堆栈,把这两个名词放到一起。其实,堆是堆,栈是栈,两种
C++常见知识掌握
nfgo
c++ 开发语言
1.Linux软件开发、调试与维护内核与系统结构Linux内核是操作系统的核心,负责管理硬件资源,提供系统服务,它是系统软件与硬件之间的桥梁。主要组成部分包括:进程管理:内核通过调度器分配CPU时间给各个进程,实现进程的创建、调度、终止等操作。使用进程描述符(task_struct)来存储进程信息,包括状态(就绪、运行、阻塞等)、优先级、内存映射等。内存管理:包括物理内存和虚拟内存管理。通过页表映
metaRTC5.0 API编程指南(一)
metaRTC
metaRTC c++ c语言 webrtc
概述metaRTC5.0版本API进行了重构,本篇文章将介绍webrtc传输调用流程和例子。metaRTC5.0版本提供了C++和纯C两种接口。纯C接口YangPeerConnection头文件:include/yangrtc/YangPeerConnection.htypedefstruct{void*conn;YangAVInfo*avinfo;YangStreamConfigstreamco
sublime个人设置
bawangtianzun
sublime text 编辑器
如何拥有jiangly蒋老师同款编译器(sublimec++配置竞赛向)_哔哩哔哩_bilibiliSublimeText4的安装教程(新手竞赛向)-知乎(zhihu.com)创建文件自动保存为c++打开SublimeText软件。转到"Tools"(工具)>"Developer"(开发者)>"NewPlugin"(新建插件)。在打开的新文件中,粘贴以下代码:importsublimeimport
Rust是否会取代C/C++?Rust与C/C++的较量
AI与编程之窗
源码编译与开发 rust c语言 c++ 内存安全 并发编程 代码安全 性能优化
目录引言第一部分:Rust语言的优势内存安全性并发性性能社区和生态系统的成长第二部分:C/C++语言的优势和地位历史积淀和成熟度广泛的库和工具支持性能优化和硬件控制丰富的行业应用社区和行业支持第三部分:挑战和阻碍学习曲线现有代码库的迁移成本生态系统和工具链的完善度社区和人才培养行业应用和推广法规和标准化第四部分:未来趋势和可能性行业趋势教育和人才培养兼容和共存行业标准化企业支持和应用开源社区和生态
python可以制作大型游戏_python能做游戏吗-python能开发游戏吗
靖dede
python可以制作大型游戏
python可以写游戏,但不适合。下面我们来分析一下具体原因。用锤子能造汽车吗?谁也没法说不能吧?历史上也确实曾经有些汽车,是用锤子造出来的。但一般来说,还是用工业机器人更合适对吗?比较大型的,使用Python的游戏有两个,一个是《EVE》,还有一个是《文明》。但这仅仅是个例,没有广泛意义。一般来说,用来做游戏的语言,有两种。一是C++。。一是C#。。Python理论上,不仅不适合做游戏,而是只要
Python开发游戏?也太好用了吧
七步编程
工具 Github python python 游戏 开发语言
程序员宝藏库:https://gitee.com/sharetech_lee/CS-Books-Store当然可以啦!现在日常能够用到和想到的场景,绝大多数都可以用Python实现。效果怎么样暂且不提,但是得益于丰富的第三方工具包,的确让Python能够很容易处理各种各样的场景。对于游戏开发也是这样,如果真的要想商业化,Python在游戏开发方面肯定没办法和C++相提并论,但是如果用于日常学习和自
Go编程语言前景怎么样?参加培训好就业吗
QFdongdong
Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。不仅可以开发web,可以开发底层,目前知乎就是用golang开发。区块链首选语言就是go,以-太坊,超级账本都是基于go语言,还有go语言版本的btcd.Go的目标是希望提升现有编程语言对程序库等依赖性(dependency)的管理,这些软件元素会被应用程序反复调用。由
OpenCV图像处理技术(Python)——入门
森屿_
opencv
©FuXianjun.AllRightsReserved.OpenCV入门图像作为人类感知世界的视觉基础,是人类获取信息、表达信息的重要手段,OpenCV作为一个开源的计算机视觉库,它包括几百个易用的图像成像和视觉函数,既可以用于学术研究,也可用于工业邻域,它于1999年由因特尔的GaryBradski启动,OpenCV库主要由C和C++语言编写,它可以在多个操作系统上运行。1.1图像处理基本操作
linux gcc 格式,Linux下gcc与gdb简介
神奇的战士
linux gcc 格式
gcc编译器可以将C、C++等语言源程序、汇编程序编译、链接成可执行程序。gdb是GNU开发的一个Unix/Linux下强大的程序调试工具。linux下没有后缀名的概念。但gcc根据文件的后缀来区别输入文件的类别:.cC语言源代码文件.a由目标文件构成的库文件.C、.cc、.cppC++源码文件.h头文件.i经过预处理之后的C语言文件.ii经过预处理之后的C++文件.o编译后的目标文件.s汇编源码
浅谈openresty
爱编码的钓鱼佬
nginx openresty 运维
熟悉了nginx后再来看openresty,不得不说openresty是比较优秀的。对nginx和openresty的历史等在这此就不介绍了。首先对标nginx,自然有优劣一、开发难度nginx:毫无疑问nginx的开发难度比较高,需要扎实的c/c++基础,而且还需要对nginx源码比较熟悉,开发效率慢,比如实现一个类似echo的功能,至少要上百行代码。而openresty只需要一句ngx.say
Lua 与 C#交互
z2014z
lua c# 开发语言
Lua与C#交互前提Lua是一种嵌入式脚本语言,Lua的解释器是用C编写的,因此可以方便的与C/C++进行相互调用。轻量级Lua语言的官方版本只包括一个精简的核心和最基本的库,这使得Lua体积小、启动速度快,也适合嵌入在别的程序里。交互过程C#调用Lua:由C#文件调用Lua解析器底层dll库(由C语言编写),再由dll文件执行相应的Lua文件。Lua调用C#:1、Wrap方式:首先生成C#源文件
Java【泛型】
SkyrimCitadelValinor
Java基础 java
Java泛型的概述不同类的数据如果封装方法相同,不必为每一种类单独定义一个类,只需定义一个泛型类,减少类的声明,提高编程效率。通过准确定义泛型类,可避免对象类型转换时产生的错误。泛型又提供了一种类型安全检测机制,只有数据类型相匹配的变量才能正常的赋值,否则编译器就不通过。Java中的泛型与C++类模板的作用相同,但是编译方式不同,Java泛型类只会生成一部分目标代码,牺牲运行速度,而C++的类模板
VMware Workstation 11 或者 VMware Player 7安装MAC OS X 10.10 Yosemite
iwindyforest
vmware mac os 10.10 workstation player
最近尝试了下VMware下安装MacOS 系统,
安装过程中发现网上可供参考的文章都是VMware Workstation 10以下, MacOS X 10.9以下的文章,
只能提供大概的思路, 但是实际安装起来由于版本问题, 走了不少弯路, 所以我尝试写以下总结, 希望能给有兴趣安装OSX的人提供一点帮助。
写在前面的话:
其实安装好后发现, 由于我的th
关于《基于模型驱动的B/S在线开发平台》源代码开源的疑虑?
deathwknight
JavaScript java 框架
本人从学习Java开发到现在已有10年整,从一个要自学 java买成javascript的小菜鸟,成长为只会java和javascript语言的老菜鸟(个人邮箱:
[email protected] )
一路走来,跌跌撞撞。用自己的三年多业余时间,瞎搞一个小东西(基于模型驱动的B/S在线开发平台,非MVC框架、非代码生成)。希望与大家一起分享,同时有许些疑虑,希望有人可以交流下
平台
如何把maven项目转成web项目
Kai_Ge
maven MyEclipse
创建Web工程,使用eclipse ee创建maven web工程 1.右键项目,选择Project Facets,点击Convert to faceted from 2.更改Dynamic Web Module的Version为2.5.(3.0为Java7的,Tomcat6不支持). 如果提示错误,可能需要在Java Compiler设置Compiler compl
主管???
Array_06
工作
转载:http://www.blogjava.net/fastzch/archive/2010/11/25/339054.html
很久以前跟同事参加的培训,同事整理得很详细,必须得转!
前段时间,公司有组织中高阶主管及其培养干部进行了为期三天的管理训练培训。三天的课程下来,虽然内容较多,因对老师三天来的课程内容深有感触,故借着整理学习心得的机会,将三天来的培训课程做了一个
python内置函数大全
2002wmj
python
最近一直在看python的document,打算在基础方面重点看一下python的keyword、Build-in Function、Build-in Constants、Build-in Types、Build-in Exception这四个方面,其实在看的时候发现整个《The Python Standard Library》章节都是很不错的,其中描述了很多不错的主题。先把Build-in Fu
JSP页面通过JQUERY合并行
357029540
JavaScript jquery
在写程序的过程中我们难免会遇到在页面上合并单元行的情况,如图所示
如果对于会的同学可能很简单,但是对没有思路的同学来说还是比较麻烦的,提供一下用JQUERY实现的参考代码
function mergeCell(){
var trs = $("#table tr");
&nb
Java基础
冰天百华
java基础
学习函数式编程
package base;
import java.text.DecimalFormat;
public class Main {
public static void main(String[] args) {
// Integer a = 4;
// Double aa = (double)a / 100000;
// Decimal
unix时间戳相互转换
adminjun
转换 unix 时间戳
如何在不同编程语言中获取现在的Unix时间戳(Unix timestamp)? Java time JavaScript Math.round(new Date().getTime()/1000)
getTime()返回数值的单位是毫秒 Microsoft .NET / C# epoch = (DateTime.Now.ToUniversalTime().Ticks - 62135
作为一个合格程序员该做的事
aijuans
程序员
作为一个合格程序员每天该做的事 1、总结自己一天任务的完成情况 最好的方式是写工作日志,把自己今天完成了什么事情,遇见了什么问题都记录下来,日后翻看好处多多
2、考虑自己明天应该做的主要工作 把明天要做的事情列出来,并按照优先级排列,第二天应该把自己效率最高的时间分配给最重要的工作
3、考虑自己一天工作中失误的地方,并想出避免下一次再犯的方法 出错不要紧,最重
由html5视频播放引发的总结
ayaoxinchao
html5 视频 video
前言
项目中存在视频播放的功能,前期设计是以flash播放器播放视频的。但是现在由于需要兼容苹果的设备,必须采用html5的方式来播放视频。我就出于兴趣对html5播放视频做了简单的了解,不了解不知道,水真是很深。本文所记录的知识一些浅尝辄止的知识,说起来很惭愧。
视频结构
本该直接介绍html5的<video>的,但鉴于本人对视频
解决httpclient访问自签名https报javax.net.ssl.SSLHandshakeException: sun.security.validat
bewithme
httpclient
如果你构建了一个https协议的站点,而此站点的安全证书并不是合法的第三方证书颁发机构所签发,那么你用httpclient去访问此站点会报如下错误
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path bu
Jedis连接池的入门级使用
bijian1013
redis redis数据库 jedis
Jedis连接池操作步骤如下:
a.获取Jedis实例需要从JedisPool中获取;
b.用完Jedis实例需要返还给JedisPool;
c.如果Jedis在使用过程中出错,则也需要还给JedisPool;
packag
变与不变
bingyingao
不变 变 亲情永恒
变与不变
周末骑车转到了五年前租住的小区,曾经最爱吃的西北面馆、江西水饺、手工拉面早已不在,
各种店铺都换了好几茬,这些是变的。
三年前还很流行的一款手机在今天看起来已经落后的不像样子。
三年前还运行的好好的一家公司,今天也已经不复存在。
一座座高楼拔地而起,
【Scala十】Scala核心四:集合框架之List
bit1129
scala
Spark的RDD作为一个分布式不可变的数据集合,它提供的转换操作,很多是借鉴于Scala的集合框架提供的一些函数,因此,有必要对Scala的集合进行详细的了解
1. 泛型集合都是协变的,对于List而言,如果B是A的子类,那么List[B]也是List[A]的子类,即可以把List[B]的实例赋值给List[A]变量
2. 给变量赋值(注意val关键字,a,b
Nested Functions in C
bookjovi
c closure
Nested Functions 又称closure,属于functional language中的概念,一直以为C中是不支持closure的,现在看来我错了,不过C标准中是不支持的,而GCC支持。
既然GCC支持了closure,那么 lexical scoping自然也支持了,同时在C中label也是可以在nested functions中自由跳转的
Java-Collections Framework学习与总结-WeakHashMap
BrokenDreams
Collections
总结这个类之前,首先看一下Java引用的相关知识。Java的引用分为四种:强引用、软引用、弱引用和虚引用。
强引用:就是常见的代码中的引用,如Object o = new Object();存在强引用的对象不会被垃圾收集
读《研磨设计模式》-代码笔记-解释器模式-Interpret
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
package design.pattern;
/*
* 解释器(Interpreter)模式的意图是可以按照自己定义的组合规则集合来组合可执行对象
*
* 代码示例实现XML里面1.读取单个元素的值 2.读取单个属性的值
* 多
After Effects操作&快捷键
cherishLC
After Effects
1、快捷键官方文档
中文版:https://helpx.adobe.com/cn/after-effects/using/keyboard-shortcuts-reference.html
英文版:https://helpx.adobe.com/after-effects/using/keyboard-shortcuts-reference.html
2、常用快捷键
Maven 常用命令
crabdave
maven
Maven 常用命令
mvn archetype:generate
mvn install
mvn clean
mvn clean complie
mvn clean test
mvn clean install
mvn clean package
mvn test
mvn package
mvn site
mvn dependency:res
shell bad substitution
daizj
shell 脚本
#!/bin/sh
/data/script/common/run_cmd.exp 192.168.13.168 "impala-shell -islave4 -q 'insert OVERWRITE table imeis.${tableName} select ${selectFields}, ds, fnv_hash(concat(cast(ds as string), im
Java SE 第二讲(原生数据类型 Primitive Data Type)
dcj3sjt126com
java
Java SE 第二讲:
1. Windows: notepad, editplus, ultraedit, gvim
Linux: vi, vim, gedit
2. Java 中的数据类型分为两大类:
1)原生数据类型 (Primitive Data Type)
2)引用类型(对象类型) (R
CGridView中实现批量删除
dcj3sjt126com
PHP yii
1,CGridView中的columns添加
array(
'selectableRows' => 2,
'footer' => '<button type="button" onclick="GetCheckbox();" style=&
Java中泛型的各种使用
dyy_gusi
java 泛型
Java中的泛型的使用:1.普通的泛型使用
在使用类的时候后面的<>中的类型就是我们确定的类型。
public class MyClass1<T> {//此处定义的泛型是T
private T var;
public T getVar() {
return var;
}
public void setVa
Web开发技术十年发展历程
gcq511120594
Web 浏览器 数据挖掘
回顾web开发技术这十年发展历程:
Ajax
03年的时候我上六年级,那时候网吧刚在小县城的角落萌生。传奇,大话西游第一代网游一时风靡。我抱着试一试的心态给了网吧老板两块钱想申请个号玩玩,然后接下来的一个小时我一直在,注,册,账,号。
彼时网吧用的512k的带宽,注册的时候,填了一堆信息,提交,页面跳转,嘣,”您填写的信息有误,请重填”。然后跳转回注册页面,以此循环。我现在时常想,如果当时a
openSession()与getCurrentSession()区别:
hetongfei
java DAO Hibernate
来自 http://blog.csdn.net/dy511/article/details/6166134
1.getCurrentSession创建的session会和绑定到当前线程,而openSession不会。
2. getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭。
这里getCurrentSession本地事务(本地
第一章 安装Nginx+Lua开发环境
jinnianshilongnian
nginx lua openresty
首先我们选择使用OpenResty,其是由Nginx核心加很多第三方模块组成,其最大的亮点是默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序。而且OpenResty提供了大量组件如Mysql、Redis、Memcached等等,使在Nginx上开发Web应用更方便更简单。目前在京东如实时价格、秒
HSQLDB In-Process方式访问内存数据库
liyonghui160com
HSQLDB一大特色就是能够在内存中建立数据库,当然它也能将这些内存数据库保存到文件中以便实现真正的持久化。
先睹为快!
下面是一个In-Process方式访问内存数据库的代码示例:
下面代码需要引入hsqldb.jar包 (hsqldb-2.2.8)
import java.s
Java线程的5个使用技巧
pda158
java 数据结构
Java线程有哪些不太为人所知的技巧与用法? 萝卜白菜各有所爱。像我就喜欢Java。学无止境,这也是我喜欢它的一个原因。日常
工作中你所用到的工具,通常都有些你从来没有了解过的东西,比方说某个方法或者是一些有趣的用法。比如说线程。没错,就是线程。或者确切说是Thread这个类。当我们在构建高可扩展性系统的时候,通常会面临各种各样的并发编程的问题,不过我们现在所要讲的可能会略有不同。
开发资源大整合:编程语言篇——JavaScript(1)
shoothao
JavaScript
概述:本系列的资源整合来自于github中各个领域的大牛,来收藏你感兴趣的东西吧。
程序包管理器
管理javascript库并提供对这些库的快速使用与打包的服务。
Bower - 用于web的程序包管理。
component - 用于客户端的程序包管理,构建更好的web应用程序。
spm - 全新的静态的文件包管
避免使用终结函数
vahoa.ma
java jvm C++
终结函数(finalizer)通常是不可预测的,常常也是很危险的,一般情况下不是必要的。使用终结函数会导致不稳定的行为、更差的性能,以及带来移植性问题。不要把终结函数当做C++中的析构函数(destructors)的对应物。
我自己总结了一下这一条的综合性结论是这样的:
1)在涉及使用资源,使用完毕后要释放资源的情形下,首先要用一个显示的方