C++编程细节(持续更新 )
1. \n 与 endl 的区别
\n和endl均有换行功能,区别在于:
endl比’\n’多了一个“刷新”流缓冲的flush操作。以文件输出流为例:当流的缓冲区未满时,写入’\n’不会马上写到文件里,但执行endl会“强行”把缓冲区的内容写入文件。故endl不仅换行,还刷新流缓冲。并且在VS2015中按F12转到endl定义,写着insert newline and flush stream。综上,\n比endl高效。
在不同操作系统中,endl比\n可移植性要好(有待探索)。
2. sizeof函数
定义
sizeof是一个操作符(operator)。
其作用是返回一个对象或类型所占的内存字节数。
语法
1) sizeof (object); //sizeof (对象)
2) sizeof object; //sizeof 对象
3) sizeof (type_name); //sizeof (类型)
说明
1. 对象可以是各种类型的变量,以及表达式。一般sizeof不会对表达式进行计算。
2. sizeof对对象求内存大小,最终都是转换为对对象的数据类型进行求值。
3. sizeof (表达式); //值为表达式的最终结果的数据类型的大小。
实例
int i;
cout << sizeof(int) << "\n"; //值为4
cout << sizeof(i) << "\n"; //值为4,等价于sizeof(int)
cout << sizeof i << "\n"; //值为4
cout << sizeof(2) << "\n";
//值为4,等价于sizeof(int),因为2的类型为int
cout << sizeof(2 + 3.14) << "\n";
//值为8,等价于sizeof(double),因为此表达式的结果的类型为double
了解更多:c++中sizeof()的用法介绍
3. float保留7位有效数字的问题
- 有效位包括整数部分和小数部分,不包括小数点。
- float的有效位数是6位 或 7位,具体取决于编译器。
- double的有效位数是15位 或 16位,具体取决于编译器。
- %f默认保留小数点后六位。
float f1 = 7.123456789;
float f2 = 7.123456885;
float f3 = 7.123459;
cout << (f1 != f2 ? "not same\n" : "same\n");//same,前7位相等,均为7.123456
cout << (f1 != f3 ? "not same\n" : "same\n");//not same,前7位不等
//说明float的有效位数是7位
double d1 = 12345.123456789012345;
double d2 = 12345.123456789012222;
double d3 = 12345.123456789022222;
cout << (d1 != d2 ? "not same\n" : "same\n");//same
cout << (d1 != d3 ? "not same\n" : "same\n");//not same
//说明double的有效位数是16位
其他例子:
float f = 12.123456789;
float m = 12.123466666;//与f前6位相同
float x = 12.123457999;//与f前7位相同
float y = 12.123457001;//与f前7位相同
printf("%f\n", f);//12.123457
cout << f << "\n";//12.1235
cout << m << "\n";//12.1235
cout << x << "\n";//12.1235
//printf与cout都进行了四舍五入,不过%f默认保留小数点后六位,C++默认流输出数值有效位是6。
cout << (f != m ? "not same\n" : "same\n");//not same
cout << (f != x ? "not same\n" : "same\n");//not same
cout << (f != y ? "not same\n" : "same\n");//same
//这里分析为,两浮点数之差接近0,就认为相等,否则不等。
了解更多:关于float有效位数为7位的研究
4. string类元素存取
可使用下标操作符[]和函数at()对元素包含的字符进行访问。但要注意操作符[]并不检查索引是否有效(有效索引0~str.length()-1),如果索引失效,会引起未定义的行为。而at()会检查,如果使用at()的时候索引无效,会抛出out_of_range异常。
有一个例外,const string a;的操作符[]对索引值a.length()仍然有效,其返回值是’\0’。其他的各种情况,a.length()索引都是无效的。
举例如下:
const string Cstr("const string");
string Str("string");
//两种方法都可以存取元素
cout<< Str[3]; //ok
cout<< Str.at(3); //ok
//无效索引时的情况
cout << Str[100]; //未定义的行为
cout << Str.at(100); //throw out_of_range
cout<< Str[Str.length()]; //【未定义行为=>似乎也返回'\0'(有待理论研究)】
cout<< Cstr[Cstr.length()];//返回'\0'
Str.at(Str.length());//throw out_of_range
Cstr.at(Cstr.length()); //throw out_of_range
不赞成类似于下面的引用或指针赋值:
char& r=s[2];
char* p= &s[3];
因为一旦发生重新分配,r,p立即失效。避免的方法就是不使用。
string类函数列表
了解更多:C++之string类型详解
5. string类的size()与length()函数
C++ string的size()和length()函数没有区别
C++标准库中的string中两者的源代码如下:
size_type __CLR_OR_THIS_CALL length() const
{ // return length of sequence
return (_Mysize);
}
size_type __CLR_OR_THIS_CALL size() const
{ // return length of sequence
return (_Mysize);
}
所以两者没有区别。
length是因为沿用C语言的习惯而保留下来的,string类最初只有length,引入STL之后,为了兼容又加入了size,它是作为STL容器的属性存在的,便于符合STL的接口规则,以便用于STL的算法。
string类的size()/length()方法返回的是字节数,不管是否有汉字。
参考文章:C++ string的size()和length()函数没有区别
6. sort()函数
- sort函数可以三个参数也可以两个参数;
- 必须的头文件是#include < algorithm>和using namespace std;
- 它使用的排序方法是类似于快排的方法,时间复杂度为n*log2(n);
- sort函数有三个参数:(第三个参数可不写)
1)第一个是要排序的数组的起始地址;
2)第二个是结束的地址(最后一位要排序的地址);
3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。
两个参数的用法:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void main() {
int a[] = { 33,61,12,19,14,71,78,59 };
vector<int> aa(a, a + 8);
sort(aa.begin(), aa.end());//两个参数
for (vector<int>::iterator it = aa.begin(); it != aa.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//运行结果:12 14 19 33 59 61 71 78(默认升序排列)
三个参数的用法:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool descend(int i, int j) {
return (i>j);
}
void main() {
int a[] = { 33,61,12,19,14,71,78,59 };
vector<int> aa(a, a + 8);
sort(aa.begin(), aa.end(),descend);//三个参数
for (vector<int>::iterator it = aa.begin(); it != aa.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//运行结果:78 71 61 59 33 19 14 12(降序排列)
sort和stable_sort的区别
- sort是快速排序实现,因此是不稳定的;stable_sort是归并排序实现,因此是稳定的;
- 对于相等的元素sort可能改变顺序,stable_sort保证排序后相等的元素次序不变;
- 如果提供了比较函数,sort不要求比较函数的参数被限定为const,而stable_sort则要求参数被限定为const,否则编译不能通过。
参考文章:c++中sort()函数的用法
C++中sort和stable_sort的区别
7. clock函数
定义
clock_t clock( void );
说明
- clock() 函数返回从开启这个程序进程到程序中调用clock()函数时之间的CPU时钟计时单元(clock tick)数,也称挂钟时间(wal-clock)。
- typedef long clock_t,clock_t实际为long 类型。
- 常量CLOCKS_PER_SEC表示一秒会有多少个时钟计时单元,其定义如下:
// The number of clock ticks per second
#define CLOCKS_PER_SEC ((clock_t)1000)
#define CLK_TCK CLOCKS_PER_SEC
用法
#include <iostream>
#include <time.h>
using namespace std;
int main(int argc, char** argv) {
clock_t start = clock();
for (int i = 0; i < 10000; i++)
cout << i << " ";
clock_t finish = clock();
double duration = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\nThe duration is " << duration << "seconds" << endl;
system("pause");
}
运行结果:
8. 常数后缀说明
1.数值常数有:整型常数、浮点常数;
2.只有数值常数才有后缀说明;
3.数值常数后缀不区分字母大小写。
1)整型常数的表示形式有:十进制形式、以0开头的八进制形式、以0x开头的十六进制形式,无二进制形式。
整型常数默认是signed int的。
对整型常数进行类型转换的后缀只有:u或U(unsigned)、l或L(long)、u/U与l/L的组合(如:ul、lu、Lu等)。例:100u; -123u; 0x123l;
2)浮点常数的表示形式有:科学计数形式和小数点形式。
浮点常数默认是double的。
对浮点常数进行类型转换的后缀只有:f或F(单精度浮点数)、l或L(长双精度浮点数)。(注:因浮点型常数总是有符号的,故没有u或U后缀)。例:1.23e5f; 1.23l; -123.45f;
参考文章:c++ 常数后缀说明
9. cin.ignore()
cin.ignore()用于输入流,其调用形式为cin.ignore(n,终止字符)。函数原型如下:
istream &ignore( streamsize num = 1, int delim = EOF );
作用: 跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包括终止字符在内的若干字符)。
cin.ignore(1024.’\n’);
效果: 第一个参数设置的足够大,表示将回车(包括回车)之前的数据全部清除,此举在保证回车符之前的数据被使用后,可以当作刷新输入缓冲区的方式使用。
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
char array[20];
//cin.ignore(); //默认 n = 1,忽略第一个字符:Apple => pple
//cin.ignore(8, 'e');//遇到指定结束符'e'而结束:ApplePie => Pie
cin.ignore(3, 'e');//忽略掉3个字符而结束:ApplePie => lePie
cin.getline(array, 8);
cout << array << endl;
}
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int a, b, c;
cout << "input a:";
cin >> a;
cin.ignore(1024, '\n');
cout << "input b:";
cin >> b;
cin.ignore(1024, '\n');
cout << "input c:";
cin >> c ;
cout << a << "\n" << b << "\n" << c << endl;
//如果没有cin.ignore(),可以一次输入3个数,用空格隔开,但是不美观
}
/*运行结果:
input a:12 34 56
input b:78 90 92
input c:98
12
78
98
*/
参考文章:ignore函数——百度百科
一眼就能看懂的cin.ignore()函数详解
C++ cin.ignore() 的使用
std::istream::ignore() 函数
10. 强制类型转换
强制类型转换的一般形式为: (类型名)(表达式) 。
注意: 如果要进行强制类型转换的对象是一个变量,该变量可以不用括号括起来。如果要进行强制类型转换的对象是一个包含多项的表达式,则表达式应该用括号括起来。
以上强制类型转换的形式是原来C语言使用的形式,C++把它保留了下来,以利于兼容。
C++还增加了以下形式:类型名(表达式) ,如 int(x) 或 int(x+y) 。
类型名不加括号,而变量或表达式用括号括起来。这种形式类似于函数调用。但许多人仍习惯于用第一种形式,把类型名包在括号内,这样比较清楚。
在强制类型转换时,得到一个所需类型的中间变量,但原来变量的类型未发生变化。
参考文章:C++强制类型转换
11. seekg()用法
seekp:设置输出文件流的文件流指针位置,p是put缩写
seekg:设置输入文件流的文件流指针位置,g是get缩写
seekg()
Moves the read position in a stream.//移动在流中读的位置
------------------------------------------------------------------------
basic_istream<Elem, Tr>& seekg( //一个参数
pos_type pos
);
basic_istream<Elem, Tr>& seekg( //两个参数
off_type off,
ios_base::seekdir way
);
------------------------------------------------------------------------
//参数
pos
The absolute position in which to move the read pointer.
//移动读取指针的绝对位置
off //偏移量
An offset to move the read pointer relative to way.
way // 基地址
One of the ios_base::seekdir enumerations.
//它有三个取值
//ios::beg:表示输入流的开始位置
//ios::cur:表示输入流的当前位置
//ios::end:表示输入流的结束位置
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
ifstream in("test.txt");//test.txt内容为:0123456789abcdefg
char c1, c2, c3,c4,c5;
in.seekg(0);
in >> c1;//c1 = 0
in.seekg(6);
in >> c2;//c2 = 6
in.seekg(4, ios::beg);
in >> c3;//c3 = 4
in.seekg(-1, ios::end);
in >> c4;//c4 = g
in.seekg(2, ios::end);//一旦指针到达文件末尾,就无法再将指针指向文件其他位置
in >> c5;//c5 = 空
cout << "c1 = " << c1 << "\n"
<< "c2 = " << c2 << "\n"
<< "c3 = " << c3 << "\n"
<< "c4 = " << c4 << "\n"
<< "c5 = " << c5 << "\n" << endl;
}
/*运行结果:
c1 = 0
c2 = 6
c3 = 4
c4 = g
c5 =
*/
参考文章:C++ seekg()函数
c++ fstream中seekg()和seekp()的用法
12. STL函数
count()函数
- 形式:count(first,last,value),返回int型。
- first是容器的首迭代器,last是容器的末迭代器,value是询问的元素。
- 功能:统计容器中等于value元素的个数。
set_difference()函数
用于求两个集合的差集,结果集合中包含所有属于第一个集合但不属于第二个集合的元素。
由于区间S1,S2内的每个元素都不需唯一,因此,如果某个值在S1中出现m次,在S2中出现n次,那么该值在输出区间中出现的次数为max(m-n,0),且全部来自S1。
重载1
OutputIterator set_difference(
InputIterator1 first1,
InputIterator1 last1,
InputIterator2 first2,
InputIterator2 last2,
OutputIterator result
);
注:需保证第一集合和第二集合有序,并从小到大排序,内部使用默认“<”操作符比较元素大小;
重载2
OutputIterator set_difference(
InputIterator1 first1,
InputIterator1 last1,
InputIterator2 first2,
InputIterator2 last2,
OutputIterator result,
Compare comp
);
注:需保证第一集合和第二集合有序,排序方式参照Compare确定,内部使用Compare比较元素大小。
参数:first1、last1分别是第一个集合的开始位置、结束位置,first2、last2分别是第二个集合的开始位置、结束位置,result是结果集合的插入迭代器。
返回值:结果集合的结束位置迭代器。
说明:
1.第五个参数可有多重版本。
2.set_difference(A.begin(),A.end(),B.begin(),B.end(),inserter( C1 , C1.begin() ) );第五个参数的意思是将集合A、B取差集后的结果存入集合C中。
3.set_difference(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator(cout," “)); 第五个参数的意思是将集合A、B取差集后的结果直接输出,(cout,” ")双引号里面是用来间隔集合元素的符号或空格。
实现:
#include <iterator>
#include <iostream>
#include<algorithm>
using namespace std;
int main(int argc, char** argv) {
string s = "14325";
string t = "21";
string C1;
sort(s.begin(), s.end());//12345
sort(t.begin(), t.end());//12
set_difference(s.begin(), s.end(), t.begin(), t.end(), inserter(C1, C1.begin()));
cout << C1 << endl;//345
set_difference(s.begin(), s.end(), t.begin(), t.end(), ostream_iterator<char>(cout, " "));
cout << endl;//3 4 5
}
参考文章:关于C++里set_difference的使用总结
STL之交集、并集、差集
C++集合操作之集合并集:std::set_difference
set_difference的使用
13. find函数
功能:在字符串中查找某字符或某子串,如果找到,就返回字符在字符串中的位置或子串在字符串中第一次出现的位置,否则返回string::npos,即 -1。
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
string s1 = "abcdefjklmn";
int pos1 = s1.find('d'); //在s1中查找字符'd',返回下标3
int pos2 = s1.find("de");//在s1中查找子串"de",返回下标3
int pos3 = s1.find("d", 3);//在s1中查找子串"de",从下标3开始查找,返回下标3
int pos4 = s1.find("uvw");//在s1中查找子串"uvw",返回-1
//判断s1是否包含子串"lmn"
if (s1.find("lmn") == string::npos)
cout << "Not" << endl;
else
cout << "Yes" << endl;
}