C++知识总结
来源:互联网 发布:软件实施部门管理制度 编辑:程序博客网 时间:2024/05/12 09:51
1、类型转化
显式类型转换被称为“强制类型转换”(cast)
标准C++中有四个类型转换符:static_cast、dynamic_cast、reinterpret_cast、和const_cast。
1)static_cast:
用法:static_cast< type-id > ( expression )
说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
需要static_cast强制转换的情况:
情况1:void指针->其他类型指针
情况2:改变通常的标准转换
情况3:避免出现可能多种转换的歧义
它主要有如下几种用法:
用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
把void指针转换成目标类型的指针(不安全!!)
把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
static_cast把子类的指针或引用转换成基类
2)dynamic_cast
用法:dynamic_cast< type-id > ( expression )
说明:该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
解释:基类中如果没有f()函数或者是没将该函数设置为virtual函数时,此时又在子类中有该方法,利用
dynamic_cast可以将子类的一个对象传给某个函数(参数为基类),此时这个基类就可以访问这个f()函数
class基类{
//或者没有virtualf();
};
class子类{
f();
}
voidpayroll(基类 *pe){
子类 *pm =dynamic_cast<子类 *>(pe);
//如果pe实际指向一个Programmer对象,dynamic_cast成功,并且开始指向Programmer对象起始处
pm->f();//此时这里可以
}
payroll(子类);//但这个时候的实参必须为子类
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。dynamic_cast还支持交叉转换(cross cast)。如
子类2 *pd2 =static_cast<子类2 *>(子类1对象); //compile error
子类2 *pd2 =dynamic_cast<子类2 *>(子类1对象); //pd2 is NULL
2、文件读写和字符串流
ifstream in("address.txt");
for(string s; getline(in,s);)
{
//以字符串数组作为输入源
istrstream in1(s.c_str(), 0);
pstu p=new stu;
in1>>(*p).m_no>>(*p).m_name>>(*p).m_sex>>(*p).m_age>>(*p).m_tel>>(*p).m_address>>(*p).m_qq;
p->m_next =NULL;
(*head).m_next=p;
head=p;
}
字符串流
他们创建对象就必须包含strstream.h头文件。
istrstream类用于执行C风格的串流的输入操作,也就是以字符串数组作为输入设备。
istrstreamin1(s.c_str(), 0);或者是istrstream in1(s.c_str(), s.length+1);
参数1表示字符串数组,而参数2表示数组大小,当size为0时,表示istrstream类对象直接连接到由str所指向的内存空间并以/0结尾的字符串。
ostrstream类用于执行C风格的串流的输出操作,也就是一字符串数组作为输出设备。
ostrstream::ostrstream(char *_Ptr,int streamsize,int Mode= ios::out);
第一个参数是字符数组输出到这个字符串里面去,第二个是说明数组的大小,第三个参数是指打开方式。
如char *pbuffer=newchar[arraysize];
ostrstream ostr(pbuffer,arraysize,ios::out);
ostr<<arraysize<<ends;
strstream类同时可以支持C风格的串流的输入输出操作。
3、名空间
往名空间nameH里加东西时,我们只需要再次定义相同的名空间名。
namespacenameH
{
new
}
4、变量名取名
变量名由作用域前缀+类型前缀+一个或多个单词组成。为便于界定,每个单词的首字母要大写。
作用域前缀 说明
无 局部变量
m_ 类的成员变量(member)
sm_ 类的静态成员变量(staticmember)
s_ 静态变量(static)
g_ 外部全局变量(global)
sg_ 静态全局变量(static global)
gg_ 进程间共享的共享数据段全局变量(globalglobal)
类型前缀标明一个变量的类型,可以有如下几种:
类型前缀前缀 说明
n 整型和位域变量(number)
e 枚举型变量(enumeration)
c 字符型变量(char)
b 布尔型变量(bool)
f 浮点型变量(float)
p 指针型变量和迭代子(pointer)
pfn 特别针对指向函数的指针变量和函数对象指针(pointerof function)
g 数组(grid)
i 类的实例(instance)对于经常用到的类,也可以定义一些专门的前缀,如:std::string和std:: wstring类的前缀可以定义为"st",std::vector类的前缀可以定义为"v"等等。
如:m_nState
5、宏定义中的特殊参数(#、##、...和__VA_ARGS__)的用法(注意,这些只能用在宏定义中,不能用在其他地方)
1)__VA_ARGS__以及...
__VA_ARGS__用来代替...,比如说在一个宏中由于不知道有多少个参数,可以直接用...来表示,通过__VA_ARGS__来读取...中的内容如:#define check(...) printf(__VA_ARGS__)
如果我在下面调用
check("a= %d,b = %d", a, b);此时...代表了"a = %d,b = %d", a, b,而通过__VA_ARGS__来读取...的内容,即也为"a= %d,b = %d", a, b
即printf(__VA_ARGS__)变为printf("a= %d,b = %d", a, b)
这样,在一些情况下要定义自己的打印函数就特别方便了,比如我们要在A条件成立,且B条件不成立的情况下打印输出
#definecheck(...) printf(__VA_ARGS__)
#definecheck1(a,b,...) if (a&&!b){ printf(__VA_ARGS__);}else{printf("a =%d \n",a);}
inta = 1;
intb = 0;
check("a= %d,b = %d \n", a, b);
check1(a,b, "a = %d,b = %d \n", a, b);
结果为:a = 1,b =0
a= 1,b = 0
2)#
#作为一个预处理运算符,它可以把语言符号字符串化.例如我们定义的变量等,如果是在字符串中使用时(但是其前后必须是字符串形式),将其语言符号字符串化后并将其与左右连接起来,
如:
#defineTEST(x) printf("square of " #x" is %d.\n",(x)*(x))
intc = 10;
TEST(2);
TEST(c);
TEST(c- 2);
结果:
squareof 2 is 4.
squareof c is 100.
squareof c - 2 is 64.
特别重要的是,#和__VA_ARGS__以及...的搭配使用,可以将本不会将某些东西变为字符串形式变为字符串形式,如:
#definetoString(...) #__VA_ARGS__
char*shader = toString(
uniformsampler2D inputImageTexture;
uniformvec2 samplerSteps;
voidmain()
{
printf("ajlksajala!\n");
}
);
check(shader);
此时将uniformsampler2D inputImageTexture;
uniformvec2 samplerSteps;
voidmain()
{
printf("ajlksajala!\n");
}
变成了字符串的形式
结果为:
uniformsampler2D inputImageTexture;uniform vec2 samplerSteps;voidmain(){printf("ajlksajala!\n");}
3)##
##运算符可以用于类函数宏的替换部分.##还可以用于类对象宏的替换部分.这个运算符可以把两个语言符号组合成单个语言符号.
例如:
//在定义一个变量时
#definexName(n) var##n
//定义变量var2
intxName(2,"var");
var2= 3;
check("var2= %d \n",var2);
结果:
var2= 3
6、操纵算子
1)自带的操作算子
C++输入输出流的操纵算子(manipulator)有:endl、flush、ws、hex等。
iostream.h还包括以下的操纵算子:
int number = 10;
cout<<number<<" ";
cout<<flush; // 清空流
cout <<"16进制为:"<< hex << "0x" << number; // 输出16进制
//cin>>ws; // 跳过空格
for(int j = 0;j < 20;j++){
//右对齐,宽度为3
cout<<right<<setw(3)<<j<<endl;
}
2)自定义操纵算子
我们可能想建立自己的操纵算子,这是相当简单的。一个像endl这样的不带参数的操纵算子只是一个函数,这个函数把一个ostream引用作为它的参数。对endl的声明是:
ostream&endl(ostream&);
例子:产生一个换行而不刷新这个流。人们认为nl比使用endl要好,因为后者总是清空输出流,这可能引起执行故障。
ostream& nl(ostream& os) {
return os << "\n";
}
int main() {
cout << "newlines" << nl << "between" << nl << "each" << nl << "word" << nl;
return 0;
}
7、类里的const和enum
下面的写法有什么问题吗?:
1. class bob {
2. const size = 100; // illegal
3. int array[size]; // illegal
}
结果当然是编译不通过。why?因为const在类对象里进行了存储空间分配,编译器不能知道const的内容是什么,所以不能把它用作编译期间的常量。这意味着对于类里的常数表达式来说,const就像它在C中一样没有作用。
在类里的const意思是“在这个特定对象的寿命期内,而不是对于整个类来说,这个值是不变的”。那么怎样建立一个可以用在常数表达式里的类常量呢?
一个普通的办法是使用一个不带实例的无标记的enum。枚举的所有值必须在编译时建立,它对类来说是局部的,但常数表达式能得到它的值,这样,我们一般会看到:
class bob {
enum { size = 100}; // legal
int array[size]; // legal
}
使用enum是不会占用对象中的存储空间的,枚举常量在编译时被全部求值。我们也可以明确地建立枚举常量的值:enum { one=1,two=2,three};
8、外部变量extern
用extern声明外部变量
(1)在同一个文件内,若定义在文件中间,要在文件前面使用,可以在前面用extern声明
(2)在相关联的不同的文件内,若文件A定义了变量a(当然这个变量是全局变量),文件B要使用变量a(此时文件B已经将A文件包含进来了),若再定义变量a会出现重复定义错误,此时在文件B中只要加上extern a(不用加类型如int,但建议加上,否则可能出现不可预期的错误,实际操作中的经验),就可以使用(如果不想要某变量被另一个文件外部引用,可以加上static以防止外部引用)
9、typedef
我们可以讲Typedef当做是为某种数据类型取别名,这样我们就可以直接用这个别名来取代之前的数据类型来申明变量
Typedef数据类型 别名
typedefint INTEGER 这样在程序中可以用INTEGER代替int来进行定义,如INTERGER a;
typedef struct
{
int month;
int day;
int year;
}DATE;
这样可以用DATE定义变量:DATEbirthday; DATE *p;它代表一个结构体类型
typedef int ARR[10]; 这样用ARR a,b,c就等同于inta[10],b[10],c[10];
typedef 与#define的区别:(如typedef intCOUNT与#define COUNT int)
#define 在预编译时处理,它只能做简单的字符串替换,而typedef是编译时处理的,并非简单替换。typedef有利于程序的通用和移植
typedef int (*pcb)(int , int) ;
pcb p1,p2;
以上等价于: int(*p1)(int,int);
int (*p2)(int,int);
10、模板
类模板
template是声明类模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个,可以是类型参数,也可以是非类型参数。类型参数由关键字class或typename及其后面的标识符构成。非类型参数由一个普通参数构成,代表模板定义中的一个常量
//最简单的 template<class T>
//默认设置通用数据类型 template<class T =double>
//非类型参数 template <classtype=double, int size >
template<class T>
class templateClass{
public:
templateClass(T data){
this->data = data;
}
T data;
void print(){
cout<<data<<endl;
}
static void test();
};
模板类再调用时,要对通用数据类型初始化设置,像上面的我们可以这样初始化为: templateClass<int>
模板函数
为某个函数中设置某个量为通用的数据类型,这样就可以时不同的数据类型也可以作为这个函数的参数,调用时直接调用,不需要对这个通用数据类型初始化
template <class T>
void templateFun(T data){
cout<<data<<endl;
}
templateFun(10);
11、运算符重载
格式:
返回的数据类型 operator需要重载的符号(数据类型 &c2);
如复数的相加,在类中声明一个函数 Complex operator+(Complex &c2);
ComplexComplex::operator+(Comlex &c2) {
return Complex(real+c2.real,imag+c2.imag);
}
调用
Complexc1(1,2),c2(2,3),c3;
c3 = c1+ c2; //即可完成相加的结果
但实现重载 = 操作符的时候应该注意自我赋值 问题,如
class Bitmap{...};
class Widget{
....
private:
Bitmap *pb;
};
在这里有两种方式:
a. 证同测试 (identitytest)
Widget& Widget::operator=(const Widget& rhs)
{
if(this == &rhs) return *this;
delete pb;
pb = new Bitmap(*rhs.pb); //在这里可能会出现异常
return *this;
}
具备自我赋值安全性,但是不具备异常安全性(因为无论什么情况导致newBitmap出现错误,pb的指向都会出错)。
b. 异常安全性测试
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap *pOrig = pb;
pb = new Bitmap(*rhs.pb); //在这里可能会出现异常
delete pOrig;
return *this;
}
通常保证了异常安全性的都能够保证自我赋值安全性。
这里即便new操作抛出异常,pb还是指向原位置, 他并不高效,但是行得通。
12、 explicit(显式)
c++中的explicit关键字用来修饰类的构造函数,表明该构造函数是显式的。所谓构造函数是显示的还是隐式的说明如下
a构造函数隐式
class Myclass {
public:
Myclass(int a);
//这种为隐式
};
Myclass m = 10; 此时相当于 Myclass temp(10); Myclass m = temp;
b、构造函数显式
class Myclass {
public:
explicit Myclass(int a);
//这种为显示
};
这种情况下Myclass m =10 不会通过自动转换,会出现错误,
此时就必须
Myclass m(10);
0 0
- C一些知识总结
- C一些知识总结
- keil c 知识总结
- c语言 知识总结
- keil c 知识总结
- C Assignment2 知识总结
- C语言知识总结
- C高级知识总结
- C语言知识总结
- C语言知识总结
- C语言知识总结
- C语言知识总结
- C语言知识总结
- c语言知识总结
- 【c++】模板知识总结
- C语言程序设计知识总结
- C专栏1-知识总结
- C/C++零碎知识总结
- VMware之三种网络连接模式
- CSS学习之布局模型
- html、css、js前端开发整理
- 位图排序
- 面向对象编程的思想(2)
- C++知识总结
- Hibernate 配置文件的hbm2ddl.auto配置项
- windows网络编程入门
- Surface Pro 3电源方面注意事项
- Linux下Tomcat的安装配置
- Validate Binary Search Tree
- Linux内核分析课程6_进程创建
- xcode archive意外退出
- Android SQLite数据库使用INTO子句创建新表时报错