您的位置:首页 > 编程语言 > C语言/C++

C++中易记混知识点总结(长期更新)

2017-09-14 20:45 302 查看

C++中易记混知识点总结(长期更新)

1、const指针和引用

const int &a;//引用对象为const,但实际上只是a一厢情愿为const引用,应用对象可以为普通变量,但a仍不能改变其值(特别注意在函数形参中的使用)。
const int *a;//指向常量的指针,和上面常量引用一样是一厢情愿,注意从右向左看,a首先是一个指针,指向一个const int。
int *const a=&cc;//从右往左看,a首先是一个常对象,其次是一个指针,说明是一个常指针,常指针必须初始化,之后便不能改变。引用不是对象,不存在本身是const的引用。
不能将一个指向(引用)常量的指针(引用)赋值给一个普通指针(引用),这样有可能造成所指(引用)对象改变。

2、复杂度数组、函数声明

int *p[10];//从左往右看,首先由[10]看出p是一个数组,再看*可以知道数组中存储的是指向int的指针,可以理解为(int *) p[10],此语句定义的是一个数组。
int (*p)[10]=&cc;//先从括号内向外,p前面有*,在定义中一定是指针,所以p是指向数组的指针,数组内容为int类型,此条语句定义的就是一个指针p。
int &p[10];//没有这种写法
int (&p) [10];//数组本身是对象,所以允许定义数组的指针和引用,从内向外看,p前面有&,所以p为引用,是数组的引用。
int *p();//从右往左看,表示函数p返回值是(int*)。
int (*p)();//从内往外,p是一个指针,指向一个函数。
定义一个返回指针且指针指向一个数组的函数有两种方法:
一是使用类型别名
typedef int arrT[10];//arrT是一个类型别名,表示的类型是含有10个整数的数组,等同于using arrT=int[10]
arrT* func(int i);//此函数返回指针,指向一个arrT类型,也就是一个含10个整数的数组。
二是如下
int (*func(int i))[10];//从内向外,func返回一个指针,(*func(int i))解引用为一个数组名,所以此语句定义一个返回指针的函数,指针指向一个int 数组。

int *(&array) [10] =ptrs;//int *ptrs[10],从内向外看,array是一个引用,引用一个数组,数组内容为(int *)类型。此语句定义的是array,所以初始化给的是ptrs。

3、指向数组的指针

int *matrix[10];//定义了一个由十个int指针组成的数组。
int (*matrix) [10];//定义了一个指针,指向含有10个整数的数组。
int matrix[] [10];//同上一个,matrix[]==(*matrix)。

4、类模板和模板类一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员、默认成员函数的参数、某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。模板类其实是类模板的实例化产物,如vector和initializer_list都是类模板,而vector<int>是一个模板类。函数模板和模板函数区别类似。
5、string的一些小知识

string的size()函数返回值类型为string::size_type,这是一种能保存任意string长度的无符号整数,时常可以定义为auto n=nums.size()。
比较两个string对象,==需要长度内容完全一样,如果两个string长度不同,短的string的每个字符都与长string对应位置的字符相同,则短的<长的,如果在对应位置不一致,则比较的是第一个不一致字符的大小。
如:string a="Hello",string b="Hiya",则b>a.
字面值和string对象相加,必须保证加号的一边为string对象,有括号的按优先级来看,如:
string a="abc"
string b=a+", "是正确的
string c="sd"+"sdd"是错误的
string d=(a+"sd")+"se"括号内生成的是string对象

6、string和int的相互转换

有两种方法,一种是采用stringstream,包含在头文件sstream里。
stringstream stream;
string s;
int n;
stream<<s;
stream>>n;
第二种是采用C++11的新特性,string to_string(int a),int stoi(string a);
int n=2;
string s=to_string(n);
int a=stoi(s);

7、fstream和sstream

头文件fstream中定义了三个类fstream,ofstream,itstream,分别用来读写文件、写文件、读文件,下面代码实现了从文件中读取字符串,并直接转化为int数据,文件流中默认以空格作为间隔:
ifstream FILE;
FILE.open("execute.stdin");
vector<int> nums;
int a;
while(!FILE.eof()){
FILE>>a;
nums.push_back(a);
}FILE文件流从字符串中每次读入一个字符串给a,并根据a的类型直接将类型转化,如果FILE文件读到末尾,则流的状态会变成EOF,此时读写操作都将不成功。
类似的,头文件sstream中定义了三个类stringstream、istringstream、ostringstream,定义一个stringstream对象ss,和一个string a="sadf",利用ss的构造函数,可以用stringstream ss(a),将ss初始化,此时定义一个string temp,可以利用一下代码依次将ss中的string传递给temp:
while(!ss.eof())
ss>>temp;
也可以写成while(ss>>temp)
如果后续需要将temp转化为int,可以考虑定义一个stringstream s2,利用以下代码实现:
int a;
s2<<temp;
s2>>a;此时要注意,由于ss只有一个元素,因此在s2>>a后,s2的状态将被置为eof,不能进行任何读入读出操作,所以如果上述三行代码在循环中实现多次string到int的转化,s2将会在第一次之后失效。解决办法是使用s2.clear(),清除它的EOF状态,同时由于s2的缓存区会逐步增大(不是eof时),为了减少内存消耗,需要每次读取完毕后调用s2.str(""),将s2的缓存清空,上面两句代码都是必不可少的。为了避免上述情况,可以利用stoi和to_string来完成int和string的转化。

8、string的基本操作

string也是顺序容器,所以拥有顺序容器的许多基本操作。同时还有一些特有的操作,如截取、替换、查找。
截取:s.substr(pos,n),其中pos表示下标,不是迭代器,n表示截取的数量,返回提取的那一段,如果想删除某一段,可以将截取后的一段重新赋值给原字符串。
替换:s.replace(pos,n,s1),用s1替换s从位置pos开始的n个字符。
查找:s.find(a,pos,n),是从pos开始找目标a,a可以是一个string、char *、char,n是查找位数,可以不要。返回查找到的位置,没有找到返回-1
s.find_first_of、s.find_last_of。
翻转:algorithm头文件中,reverse(s.begin(),s.end()),此函数对其他容器也适用。

9、fstream补充知识

定义一个文件流,同时将它与一个文件关联,fstream("mytext.txt"),该语句默认用in和out模式使用文件,默认情况下,out模式打开文件默认会截断,也就是说out模式下文件内容会被丢弃,阻止其清空文件的方法是显式设置app模式,fstream("mytext.txt",ios::out|ios::app)。ios比fstream高级,可以通用。
在使用中最好将读写操作分别用ifstream和ofstream流来操作,这样可以避免文件模式的设置。如果一定要用同一个流,需要控制读、写的标记,当需要写的时候,要利用f.seekp(offset,from)设置写标记,from可以取fstream::end、beg、cur,如果用fstream::end,表示从文件末尾开始读。f.seekg()表示设置读位置,tellp()表示返回写位置,tellg()表示返回读位置。

fstream ii("test.txt",ios::in|ios::out|ios::app);
string k;
ii >> k;
ii.seekp(0,fstream::end);
ii << "111" << endl;上述代码中,test.txt中已有内容,向k中读入后,如果直接再向文件写入,写入会失败,此时需要将写标记移动到文件尾,然后写入成功。

10、关联容器不能采用sort排序

set、map内部采用红黑树,内部存储元素是pair,不是线性存储的。set和map对key进行了排序,而需要对value排序,必须将map转移到线性容器如vector等中,不能使用sort。

11、求任意对数的方法

C++中有两个对数log(ln)和log10,对于任意对数可以用上述对数相除得到,但注意用log相除可能会由于四舍五入造成偏差,所以用log10比较好。

12、关联容器迭代器用法

由于关联容器不是线性存储,因此其迭代器用法和string、vector等线性容器有所不同。如对于map s,s.begin(),要遍历下一容器只能++s.begin(),不能够s.begin()+1,因此也没有s.begin()+n的方法。

13、变量是在堆上还是栈上

一个程序内存分为五类,栈、堆、全局(静态)区、文字常量区、代码区。


栈由编译器自动释放,用于存储局部变量,函数参数等,是高地址向低地址扩展的连续内存,一般1M到2M,超出会报overflow。栈上的东西出了作用域会自动回收。如在函数中定义int a[1000][1000]便会超出。

堆是程序员自己分配释放,是用链表连接的从低到高的不连续内存区,灵活度高,容量大,但是不如栈快。用new或malloc动态分配的空间都在堆上。例如char *p=new char[2]
如上述语句在函数中,则指针p本身(一个地址)存储在栈中,而它指向的一段内存则是在堆中,同样由于vector内部采用new,因此vector<int> a(100,0),的对象a本身是在栈中(这个对象肯定包含了一个指向堆中的指针),但是它的内容,100个int则都是在堆中。对于类的对象,有如下例子阐述对象的成员是在堆中还是栈中:
class A{
vector<int> a(100,0);

};
int main(){
A b;
A *c=new A;

}

对象的成员是在堆中或栈中取决于对象本身,如果是在函数中且非动态分配内存,则对象的成员在栈中,在本例中b的成员a在栈中,但是它的100个int是在堆中,这是决定于vector内部,而c指向的对象的成员a在堆中,其100个int也在堆中。
全局变量和static均在静态区
字符串常量等在文字常量区

14、heap操作和priority_queue

heap操作定义在algorithm中,是一个操作容器的函数,priority_queue不是STL标准容器,底层采用了heap算法,而它们保存数据的容器都是vector或deque。
heap操作直接对vector进行,将存在vector中的一个完全二叉树(每一层从左到右放入vector中)进行建堆等操作。heap操作有如下几个:
int main () {
int myints[] = {10,20,30,5,15};
std::vector<int> v(myints,myints+5);

std::make_heap (v.begin(),v.end());
std::cout << "initial max heap : " << v.front() << '\n';

std::pop_heap (v.begin(),v.end()); v.pop_back();
std::cout << "max heap after pop : " << v.front() << '\n';

v.push_back(99); std::push_heap (v.begin(),v.end());
std::cout << "max heap after push: " << v.front() << '\n';

std::sort_heap (v.begin(),v.end());

std::cout << "final sorted range :";
for (unsigned i=0; i<v.size(); i++)
std::cout << ' ' << v[i];

std::cout << '\n';

return 0;
}

make_heap(begin,end),函数参数为一个vector或者deque的前后迭代器,默认less模式,也就是第一个元素是在大元素的最大堆。如要采用最小堆,第三个参数为greater<type> (),是重载的().
pop_heap(begin,end),将一个已建堆的vector的第一个元素放到end之前,并将前面元素恢复堆特性,此时最大(最小)元素并没有pop出,需要用vector.pop_back()将元素弹出。
push_heap(begin,end)是对一个刚加入新元素的堆,重建它的堆特性。
sort_heap(begin,end)是多次进行pop_heap操作,最终完成一个排序,最大堆模式下得到的排序为升序。
priority_queue的用法如下:
它除了有queue的一些用法,还有几个特殊用法。
priority<type,container type,compare type> qp;type表示优先队列中元素的类型,可以是自定义类,第二个元素为容器的类别,如vector<type>,第三个是一个比较函数类,默认为less,也就是采用最大堆,最终排序为升序,先出大的元素。如果要用最小优先队列,则需要加上greater<type>,注意这里没有括号,如果type是自定义类,则需要单独写比较类,如下,这里是从队列的角度看的,需要一个递减序列,用最小堆实现。这里重构了一个().
class myCompare{
public:
bool operator()(Node &a,Node &b){
return a.len>b.len;
}
};



pq.empty(),pq.top()取最大(最小)元素,pq.pop(),pq.push()。
15、malloc() free()和new delete的区别

malloc的参数是字节数,必须用sizeof给定类型的大小,且返回void *类型,必须强制转化为相应指针类型,如char *p=(char *) malloc(sizeof(char)*128),这语句分配了128个char的内存,在堆中。而new的用法为char *p=new char[128]。
对于自定义类,new和delete能自动调用类的构造和析构,malloc不行,但C语言只支持malloc
注意不管是new还是malloc,如果指针是局部变量,它就在栈,但是申请的空间是在堆。malloc采用空闲链表机制,延表寻找大小满足所需的内存块,将需要的大小分出,留下剩余的小部分继续在表中,这样最终就会造成表中内内存均是碎块,申请内存失败,返回NULL指针。
free之后是指针所指内存释放,指针并没有,指针是一个变量,只有随函数完毕而销毁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: