61. 字符串比较
安全的字符串比较方法:
src.compare(pos, obj.len, obj);
说明:以「obj.len」为参数,可以保证当src的子串(起始索引为「pos」)长度小于「obj.len」时,不会发生越界,而是取最长子串
示例:
60. delete
delete void* 指针是十分危险的,因为它不会调用指针实际指向的对象的析构函数,所以可能导致内存和资源泄露
59. 构造函数抛出异常
在离开构造函数的scope时,首先会析构自己的成员变量,并会递归调用父类的析构函数;不过,自己的析构函数是没有机会执行了,即便它被分配到栈上。唯一值得欣慰的是,为这个对象分配的内存,是会被释放掉的。
因此如果在构造函数中要分配一些资源,并且在执行过程中可能会抛出异常,最好用auto_ptr把它们保护起来。或者干脆不要在构造函数中执行复杂的初始化操作,转而定义一个单独的initialize方法 ...
58. const
58.1 概念
常量指针,即常量的指针,表示指针指向的对象为常量;指针常量,即指针的常量,表示指针本身是常量
常量引用,即常量的引用,表示引用绑定的对象为常量,并非引用的类型是常量
常量指针和常量引用,标准形式:
O const *p;
O const &r;
O可为普通类型和指针,当O为普通类型时,const 可提到O之前
注意:O不可为引用的原因是,当O是引用会形成& const实为引用常量,因引用本身就不可改变、自带const属性,故引用常量是为非法
58.2 分类
顶层const :不改变变量本身
底层const :不改变变量指向或绑定的对象,对应于指针和引用
58.3 类型
const T 和 T是两种不同的类型
const T t; 则&t的类型为const T*
58.4 位置
const位于*左侧,则修饰的是指针所指对象的类型;const位于*右侧,则修饰的是指针的类型
const位于&左侧,则修饰的是引用绑定到的对象的类型;const位于&右侧,则非法
即
T const *p; 等同于 const T *p;
T const &r; 等同于 const T &r;
T* const &r; 此即常量引用,表示引用绑定到的对象类型为指针常量,而指针指向的对象类型为T
58.5 要求
普通引用绑定的必须是左值,只有常量引用可以绑定右值,即
T &r = t; //t必须为左值
const T &r = t; //t左右值均可
示例
int ReturnRval(); int &r = ReturnRVal(); //非法 const int &r = ReturnRVal(); //合法
const int ival = 124; const int* &r = &ival; //非法 const int* const &r = &ival; //合法
指针类型必须与其所指对象类型一致,例外,const对象的指针可以指向非const对象
引用类型必须与其绑定对象类型一致,例外,const对象的引用可以绑定到非const对象
即
T t; T &rt = t; 则 const T &c1 = t; const T &c2 = rt; //合法
T t; T *pt = &t; 则 const T *c1 = &t; const T *c2 = pt; //合法
58.6 类和结构体
const修饰类对象或结构体,相当于所有成员变量被加上顶层const属性
若有const CnId cid; 则
cid.app类型为const std::string
cid.patt类型为const int
cid.val类型为MapVal *const
cid.box类型为TimeBox &
所以,对于const类对象或结构体,可以修改指针型成员变量指向的对象,也可以修改引用型成员变量绑定的对象,但是不可以改变成员变量本身
58.7 多级指针
可以相互赋值的前提:仅当所指对象除顶层const之外类型完全相同,且左操作数具有右操作数的全部顶层const时,两指针可以相互赋值
示例
char ** a; const char ** b; char * const * c; const char * const * d; const char * const * const e1 = d; const char* const * const e2 = b;
c = a; d = b; d = e;
57. 虚函数
某个成员函数一旦被声明成虚函数,则在所有派生类中它都是虚函数。
若派生类未覆盖虚函数,则当该类的对象调用此虚函数时,使用的是最近基类的虚函数版本。
假设存在如下继承关系:
H3——>H2——>H1
若H3未覆盖虚函数,则从直接基类H2开始逐级上访,直至最近实现。
56. 基类和派生类对象之间的类型转换
class N : public M
基类——>派生类
只能赋值:
派生类——>基类
初始化:
赋值:
理论支持
初始化和赋值一个类类型对象时,实际是在调用某个函数,即拷贝构造函数和拷贝赋值运算符。
这两个成员函数的参数类型,为const引用。
派生类中含有基类部分,故基类的引用可以绑定到派生类对象。
55. 宏定义
a. 换行符是宏定义的结束标志
b. 如果一个宏定义太长,可采用续行的方法,续行符是"\"。注意续行符之后马上回车,中间不能插入其它符号
c. 宏定义的有效范围称为宏定义名的辖域,辖域从宏定义的定义结束处开始到其所在的源程序文件末尾,穿透一切花括号(作用域)
d. 可以用预处理命令 #undef + MACRO_NAME 提前终止辖域
e. 宏一旦放入引号里面,即视为普通字符串,无特殊含义
示例
功能:向mp1和mp2中各加5组pair,简化代码,使代码简洁
讲解:
88到91行:定义宏,尽管mp2[n] = v;后存在续行符,导致91行的空白行囊括到宏定义之中,但伴随着91行结尾的回车,整个宏定义便告结束
92到96行:简洁美观地将五组数据添加到map结构中
98行:提前结束辖域,也就是说,自99行始,JAS_INSERT_MAP_VALUE即为未定义的标识符
带参宏定义,参数用法#parm,宏在函数体内被使用,parm为函数形参,则不论函数调用时使用的实参名字为何,因宏在编译阶段展开,故#parm永等于函数形参的名字,即
无论实参为何,输出总为形参t
输出永远t,形参名
55.1 #和##
背景:应用于带参宏定义
功能:#用于变量展开后,两端加双引号;##用于拼接参数
示例:
注意:井号放入引号里面,即视为普通字符,无特殊含义
54. 类型别名typedef
关键字typedef作为声明语句中的基本数据类型的一部分出现。含有typedef的声明语句定义的不再是变量而是类型别名。
例如:
typedef unsigned char uuid_t[16];
typedef unsigned char是一个整体,完整的类型名;uuid_t是类型所定义的对象名。
关键字typedef指示:定义的对象uuid_t是一个类型别名,而不是普通情况下的变量。
故uuid_t u; ===> unsigned char u[16];
53. 类型运算符
头文件:#include <typeinfo>
原型:typeid(e)
功能:返回表达式e的类型
返回值:std::type_info标准库类型,成员函数name()返回C风格字符串,表示类型名的可打印形式
示例:
结果:
非典型用例:工具函数——数字转字符串
52. 字符十六进制打印
标准格式
printf("0x%02x\n", (unsigned char)c);
51. 隐式类型转换
51.1 整型提升
在大多数表达式中,小整型一般都会提升到int类型或更大的整数类型,此过程叫整型提升。
提升过程见《Linux编程手册》35条。
50. 求值顺序
50.1 条件运算符
cond ? expr1 : expr2;
执行过程是:首先求cond的值,若cond为true,则执行expr1、放弃expr2;若cond为false,则执行expr2、放弃expr1
例
结果
50.2 &&逻辑与
expr1 && expr2
执行过程:先求expr1的值,只有当expr1为真时才继续求expr2的值,否则expr2直接跳过。
例
结果
50.3 ||逻辑或
expr1 || expr2
执行过程:先求expr1的值,只有当expr1为假时才继续求expr2的值,否则expr2直接跳过。
例
结果
49. 移位运算符
左移运算符:expr1 << expr2,将expr1左移expr2位的临时量,expr1不变;
右移运算符:expr1 >> expr2,将expr1右移expr2为的临时量,expr2不变。
左移,右侧补0;右移,依赖左侧运算对象的类型决定:无符号则左侧补0,有符号则左侧或补符号位或补0、视具体环境而定。
首先执行整形提升。
c提升成int类型,然后向左移动1位。结果
48. 成员初始化
如果没有在构造函数的初始值列表中显示地初始化成员,则该成员在构造函数体之前执行默认初始化。
默认初始化执行过程如下:
A.成员变量是内置类型,则值未定义;
B.成员变量是类类型,则调用默认构造函数。
例
结果
m_b未在AA构造函数初始值列表中显示初始化,则在进入构造函数体之前对m_b进行默认初始化,调用默认构造函数,但BB默认构造函数不存在,故报错。
47. 静态变量
47.1 静态局部变量
函数内部定义的静态变量,为静态局部变量。包括普通函数和类成员函数。
静态局部变量直到函数调用,才真正定义。
类成员函数,静态局部变量与对象无关。所有对象共享同一变量。对象销毁与否,变量不变。
结果
总结,静态局部变量都在函数初次调用时定义且仅定义一次,即静态局部变量生存期为函数首次调用直至进程退出、且与类对象无任何关系。
典型用例:统计某函数调用次数 结果
典型用例:生成序列号函数(标准)
问题:多个线程同时运行get_serial_no(),会造成重复初始化n = 0吗?
解答:任何变量的初始化都只有一次,就是在定义变量的时候。而一个变量可以声明多次,但只能定义一次,否则出现重复定义错误。定义只有一次则初始化只有一次,故static int n = 0不存在竞争。
47.2 静态成员变量
静态成员变量使用前必先全局初始化,即使用前需先定义
静态成员变量在定义时即脱离类对象而存在
46. 头文件引用问题
问题描述:类A和B互为对方成员变量,头文件互相包含
头文件
CPP文件
A.cpp存在的问题:
#include "A.h"要求先#include "B.h",#include "B.h"之前又必须有#include "A.h",死循环;若把#include "B.h"放在A.h中,则要求A.h中加#include "A.h",死循环。
B.cpp问题相同。
解决办法:超前引用
头文件
CPP文件
超前引用要求:B.h中class A被超前引用,则B.h中不能出现A类变量,只用A类指针和引用。在B.cpp引入#include "A.h"后方解除束缚。
45. 构造函数成员初始化
构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中,此时其默认构造函数负责对它进行初始化。
示例
class SqlConnPool
{
public:
SqlConnPool()
{
printf("SqlConnPool Default Constructor\n");
}
};
class SqlOpt
{
public:
int GetValue()
{
return m_iNum;
}
private:
int m_iNum;
SqlConnPool m_sqlConnPool;
};
int main()
{
SqlOpt sqlOpt;
printf("%d\n", sqlOpt.GetValue());
std::cin.get();
return 0;
}
结果
默认构造函数初始化成员变量过程:
1. 成员变量为内置类型,则值未定义;
2. 成员变量为类类型,则调用该类的默认构造函数
故:
1. 构造函数必须负责内置类型成员变量的初始化,否则出现错误;
2. 编译器合成的默认构造函数不负责内置类型成员变量初始化,故其值未定义;
3. 存在内置类型成员变量的类,不能依赖编译器合成的默认构造函数。
44. 缓冲区分配
栈空间:优点—超出作用域自动释放,缺点—大小值是编译期即可确定的,即常数
堆空间:优点—大小值能在运行期根据运算结果指定,缺点—手动释放
二者结合
示例一
/*获取缓冲区大小*/
DWORD dwLen = 0;
GetExtendedTcpTable(NULL, &dwLen, true, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
/*分配缓冲区*/
std::string sBuffer(dwLen, 0);
/*获取TcpRow*/
DWORD dwRet = GetExtendedTcpTable(&sBuffer[0], &dwLen, true, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
if(dwRet != ERROR_SUCCESS)
{
printf("GetExtendedTcpTable != ERROR_SUCCESS, ErrorCode=%d", dwRet);
return false;
}
示例二
std::string s;
s.resize(10, '1');
strcpy(&s[0], "abc");
printf("%s\n", s.c_str());
结果
43. 可以没有默认构造函数
如果我们没有为类定义任何构造函数则编译器会为我们合成默认构造函数;如果我们为类定义了任何一个构造函数则编译器不会生成默认构造函数。
类没有默认构造函数没有任何问题。
但不能使用类似:A a; 方式生成对象。
42. inet_ntoa()陷阱
sockaddr_in addr1, addr2;
addr1.sin_addr.s_addr = inet_addr("192.168.4.111");
addr2.sin_addr.s_addr = inet_addr("192.168.5.3");
printf("%s %s\n", inet_ntoa(addr1.sin_addr), inet_ntoa(addr2.sin_addr));
结果:
原因:inet_ntoa内部分配静态数组static char[]存储结果,所有前后调用存在相互覆盖。故应使用中间变量进行转存。
41.时间
41.1 时间函数
取UNIX时间戳
time_t t = time(NULL);
time_t戳转tm
tm *tm = localtime(&t);
tm转time_t
time_t t = mktime(&tm);
41.2 UNIX时间戳转字符串
char ch[100] = {0};
strftime(ch, 100, "%04Y-%02m-%02d %02H:%02M:%02S", localtime(&t));
格式:YYYY-MM-DD hh:mm:ss 月日时分秒均为两位整数
41.3 获取某时间点的UNIX时间戳
获取2014年11月5日13时47分5秒 UNIX时间戳
tm tm;
tm.tm_year = 114;
tm.tm_mon = 10;
tm.tm_mday = 5;
tm.tm_hour = 13;
tm.tm_min = 47;
tm.tm_sec = 5;
time_t t = mktime(&tm);
struct tm结构:
tm_year: 年数减1900
tm_mon: 月数减1
tm_mday、tm_hour、tm_min、tm_sec不变
41.4 智能转换
struct tm存有某一时间点,现要求获取该时间点向前800分钟的时间
方法:首先tm.tm_min -= 800; 然后 mktime(&tm);
mktime后的tm即向前800分钟时间点详细信息,无需考虑平闰年大小月。
即mktime(&tm)获取时间戳同时修改tm中的对应成员变量。
补充:获取两天前同一时刻的UNIX时间戳,假设今天12号星期三
a.tm.tm_mday -= 2;
b.tm.tm_mday = 10;
c.tm.tm_hour -= 48;
三种方式均可,mktime(&tm)获取时间戳同时修改tm中的对应成员,如tm_mday=10, tm_wday=1.但是
tm.tm_wday = 1;
tm.tm_wday -= 2;
无任何效果。
总结:
1.tm_year, tm_mon, tm_mday, tm_hour, tm_mim, tm_sec智能转换,牵一发而动全身
2.tm_wday无任何效能
40.正则表达式
例一 验证字符串格式:"yyyy-mm-dd hh:mm:ss"
#include <regex>
std::string pattern("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}");
std::regex r(pattern);
std::string str("2014-11-04 16:32:01");
bool bRes = std::regex_match(str, r);
std::cout<<std::boolalpha<<bRes<<std::endl;
例二 验证字符串格式:"hour:min:sec", hour, min, sec为一位或两位数字
#include <regex>
std::string sPattern("\\d{1,2}:\\d{1,2}:\\d{1,2}");
std::regex r(sPattern);
if(!std::regex_match(sObject, r))
std::cout<<"Not"<<std::endl;
else
std::cout<<"Ok"<<std::endl;
39.读取输入
39.1 std::cin的使用:
a.std::cin遇到空格、tab、回车自动结束读取
b.若要读取的字符串中含有空格或tab为避免之后的字符被过滤应使用
std::getline(std::cin, str);遇回车结束输入
39.2 清空输入缓冲区:fflush(stdin)
std::cin.clear()只是清标志,不是清缓冲区
38.数值极限
#include<limits>
(std::numeric_limits<DWORD>::max)()
(std::numeric_limits<ULONGLONG>::max)()
37.条件判断语句
1.condition1 && condition2作为判断条件,若condition1==false直接以false返回,condition2根本不会被执行
2.condition1 || condition2作为判断条件,若condition1==true直接以true返回,condition2不会被执行如同不存在
36.移位运算符<<
/***********************************
移位运算符:
m<<n m左移n位,移位后m值不变
************************************/
DWORD dw2 = 1;
dw2<<1;
std::cout<<dw2<<std::endl;
/***********************************
无符号数移位操作为循环移位
************************************/
DWORD dw3 = 1;
dw3 = dw3 << 32;
std::cout<<dw3<<std::endl;
结论:
1.m<<n,m左移n位
2.移位后原数不变
3.无符号数移位操作为循环移位
35.头文件引用技巧和规则
.h文件不引入而由.cpp文件引入所需头文件,可以防止头文件重复引用问题。
x.h文件没有所需的头文件,没问题。
哪个.cpp文件引用了x.h,哪个就要替它还欠下的头文件。
.cpp引用顺序:先欠下的头文件,后x.h
1.没有类型修饰符的类的成员默认为私有成员:
class A{
int num;
public:
int count;
};
num成为类A的私有成员变量。
2.引用变量声明的同时必须初始化,一旦初始化则终身为该变量的引用:
int m =14;
int& n =m; //声明的同时必须初始化
int i = 11;
n = i; //不要以为n成为i的引用,n永远是m的引用。该语句只是把i的值赋给m和n
3.引用成员变量:
class A{
int& count;
public :
A(int& m):count(m){}
void set(int& n){
count = n;
}
};
成员变量为引用,则构造函数必须对其初始化。
int main(){
int x =12;
A a(x);
int y =11;
a.set(y);
std::cout<<x<<std::endl;
}
输出:11
构造函数的参数是引用,则变量x自身被传入构造函数;成员变量是引用,则成员变量成为参数变量的引用。所以a的成员变量count就是x的引用。
调用a.set(y)修改a.count的值就是修改x。
4.结构体数组初始化:
struct stats_desc{
char *name;
char *desc;
};
int main(){
struct stats_desc stats_pool_desc[] = {
{"lisan","thief"},
{"zilong","soldier"},
{"baiqi","general"}
};
std::cout<<sizeof(stats_pool_desc)/sizeof(struct stats_desc)<<std::endl;
return 0;
}
5.变量生命期:
while(1){
char str[10000] = {0};
.............
}
str是栈变量,在语句块结束(即右大括号)时自动被销毁,故上述语句不会造成内存空间被耗光。
6.读文件:
int fd = open("txt",O_RDWR);
if(fd == -1){
std::cout<<"open file fail"<<std::endl;
return -1;
}
char ch[50] = {0};
if(read(fd, ch, 50) == -1){
std::cout<<"read file fail"<<std::endl;
return -1;
}
std::cout<<ch<<std::endl;
7.普通指针和静态指针的关系:普通指针可以赋值给静态指针,静态指针不可以赋值给普通指针。
char *ptr;
const char *cptr;
cptr = ptr; //OK
ptr = cptr; //error
静态指针不可以修改变量的值,而普通指针没有任何顾忌。如此静态指针被普通指针赋值,是说明我不会改变变量的值,属于权利的收缩。静态指针赋值给普通指针,相反不可。
8.查看错误信息:
#include <errno.h> //errno
#include <string.h> //strerror()
int main(){
int fd =socket(AF_INET,SOCK_STREAM,0);
if(fd == -1){
std::cout<<"create socket fail : "<<strerror(errno)<<std::endl;
return -1;
}
return 0;
}
9.类静态成员函数只能使用该类中的静态成员变量,只能调用该类中的静态成员函数。否则出现“without object”的错误。
10.函数模板的使用:
template<classT>
T min(T& x,T& y)
{return (x<y)?x:y;}
intmain()
{
int n1 = 2,n2 = 10;
double d1 = 1.5,d2 = 5.6;
cout<<"较小整数:"<<min(n1,n2)<<endl;
cout<<"较小实数:"<<min(d1,d2)<<endl;
system("PAUSE");
return0;
}
11.安全删除:
template<typename T>
inline void SafeDeleteArray(T*& p) {
if (NULL != p) {
delete[] p;
p = 0;
}
}
template<typename T>
inline void SafeDelete(T*& p) {
if (NULL != p) {
delete p;
p = 0;
}
}
int main(){
struct coward *rongxiaojun = new coward;
SafeDelete(rongxiaojun);
return 0;
}
12.结构体赋值:
typedef struct{
int num;
std::string name;
}info;
int main(){
info mine;
mine.num = 14;
mine.name = "rongxiaojun";
info him;
him = mine;
std::cout<<him.num<<" "<<him.name<<std::endl;
}
13.枚举类型的使用:
enum{
DID2USRNAME = 11234,
USRNAME2DID = 11235,
DID2DINFO = 11236,
};
int main(){
int msg_type = 11236;
if(msg_type == DID2DINFO)
std::cout<<”enum OK”<<std::endl;
}
枚举定义了整数。
宏定义是替换:
#define PI 3.14 //出现PI的地方自动变成3.14,则宏定义实现了数值替换
#define LEN 1024 //同上
#define IP “192.168.15.12” //出现IP的地方自动变成引号加192.168.15.12,则宏定义实现了字符串替换
14.数组作形参:
void argu(int a[]){
std::cout<<a[4]<<std::endl;
}
int main(){
int a[10];
a[4] = 12;
argu(a);
}
15.动态数组不可以初始化:
int get_num();
int main(){
int n = get_num();
char ch1[n+1]; //OK
char ch2[n] = {0}; //error
return 0;
}
16.if-else重叠结构:
int j = 2;
if(j == 1){
std::cout<<1<<std::endl;
}
else if(j == 2){
std::cout<<2<<std::endl;
}
else if(j == 3){
std::cout<<3<<std::endl;
}
17.switch case 结构:
switch (x/1000)
{
case 0:
cout << x << endl;
break;
case 1:
cout << x * 0.9 << endl;
break;
}
switch() 括号中的表达式类型:所有能转为int的类型都可以,char可以看做unsigned int,bool就是0或者1。
18.std::string str(char *buf); 申请的是栈空间。没有在堆上分配空间,故不需要释放空间。
19.宏定义可以这么用:
#define LEN 1024
char buffer[LEN] = {0} ;//宏替换整数
20.指针数组的创建:
int **p = new int *[10];
for(int i=0;i<10;i++)
p[i] = new int;
*p[6] = 123;
for(int i=0;i<10;i++)
std::cout<<*p[i]<<std::endl;
21.创建数组:
pthread_t *threads = new pthread_t[10];
//int num = sizeof(threads)/sizeof(threads[0]); 这里num=1
threads[6] = 123;
for(int i=0;i<10;i++)
std::cout<<threads[i]<<std::endl;
22.浮点数取整函数:
double ceil(double x) 返回大于或者等于指定表达式的最小整数
double floor(double x) 返回小于或者等于指定表达式的最大整数
int m = ceil(2.4); //m=3
int n = floor(2.4); //m=2
可以这么用。
23.new的用法及函数体外语句:
#include <cstring>
#include <iostream>
std::string *str = new std::string("hello");
int main(){
const char* buf = "rongxiaojun";
std::string s(buf,4);
std::string r = "rong";
r = "4"+r;
std::cout<<*str<<std::endl;
}
对比:
#include <cstring>
#include <iostream>
std::string get_str(){
return "hello";
}
std::string str = get_str();
int main(){
const char* buf = "rongxiaojun";
std::string s(buf,4);
std::string r = "rong";
r = "4"+r;
std::cout<<str<<std::endl;
}
24.memset头文件:
#include<string.h> C
#include<cstring> C++
25.
函数返回值 char *
但是 空间最好在 外层分配好
函数里面 空间会释放掉的 在函数外面使用 不安全
要不就使用 堆内存 分配
26.C/C++字符串转化为整数:atoi 头函数#include<stdlib.h>
27.extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。
28.new可以这样用?LOCAL_REV_DATA* ptr_recv_data = new LOCAL_REV_DATA;
typedef struct
{
char buf[DATA_BUFFER_SIZE];
unsigned int len;
int sfd;
}LOCAL_REV_DATA;
29. 输出变量、数值类型:#include <typeinfo> typeid(x).name()
#include <typeinfo>
unsigned long l = ULONG_MAX;
int i;
std::cout<<typeid(l).name()<<std::endl; //输出 unsigned long 或 m
std::cout<<typeid(i).name()<<std::endl; //输出 int 或 i
std::cout<<typeid(9.87).name()<<std::endl; //输出double 或 d
bool: b
char: c float : f
signed char : a double : d
unsigned char : h long double : e
short : s int : i long : l long long : x
unsigned short : t unsigned : j unsigned long : m unsigned long long : y
30. 读写文件(windows和linux下均可)
#include <fstream>
//写文件
std::ofstream a("1.txt"); //1.txt不存在则自动创建
if(a.is_open())
a<<"wobuxingle"<<std::endl;
a.close();
//读文件
std::string str;
std::ifstream b("1.txt");
if(b.is_open())
{
b>>str;
std::cout<<str<<std::endl;
}
b.close();
如果将std::endl替换为endl,可以编译可以运行但真正写入1.txt内容出现混乱。(2014.05.17 下午)
文件操作:
打开文件
文件名
注意路径名中的斜杠要双写,如:
"D:\\MyFiles\\ReadMe.txt"
文件打开方式选项:
ios::in = 0x01,//供读,文件不存在则失败(ifstream默认的打开方式)
ios::out = 0x02,//供写,文件不存在则创建,若文件已存在则清空原内容(ofstream默认的打开方式)
ios::ate = 0x04,//文件打开时,指针在文件最后。可改变指针的位置,常和in、out联合使用
ios::app = 0x08,//供写,文件不存在则创建,若文件已存在则在原文件内容后写入新的内容,指针位置总在最后
ios::trunc = 0x10,//在读写前先将文件长度截断为0(默认)
ios::nocreate = 0x20,//文件不存在时产生错误,常和in或app联合使用
ios::noreplace = 0x40,//文件存在时产生错误,常和out联合使用
ios::binary = 0x80//二进制格式文件
文件保护方式选择项:
filebuf::openprot;//默认的兼容共享方式
filebuf::sh_none;//独占,不共享
filebuf::sh_read;//读共享
filebuf::sh_write;//写共享
打开文件的方法
调用构造函数时指定文件名和打开模式
ifstream f("d:\\12.txt",ios::nocreate);//默认以 ios::in 的方式打开文件,文件不存在时操作失败
ofstream f("d:\\12.txt");//默认以 ios::out的方式打开文件
fstreamf("d:\\12.dat",ios::in|ios::out|ios::binary);//以读写方式打开二进制文件
使用Open成员函数
fstream f;
f.open("d:\\12.txt",ios::out);//利用同一对象对多个文件进行操作时要用到open函数
31. 无符号数,减法
unsigned u1;
unsigned u2;
unsigned u3;
若u2 < u1,u3 = u2 - u1,则u3为正且数值极大。
32. switch分支语句中定义局部变量问题
是不是经常遇到这种情况,在switch的某个分支处理中需要一个局部变量、顺手定义编译后,报错。错误的大体原因是
33.typeid() #include <typeinfo.h>
34.文件组织问题
条件:
1.A包含了B和C.
2.B和C互不可见(互不包含).
3.A.cpp中定义了C的对象c.
问题:
B.cpp中要用到C c,如何实现?
答案:
B.cpp按以下格式书写:
#include "C.h"
extern C c;
...(使用c)
如图: