C++个人笔记(一)

来源:互联网 发布:jdk 8 linux x64.rpm 编辑:程序博客网 时间:2024/05/22 21:25

总结:

另有一篇文章 A*算法和代码:http://blog.csdn.net/lmnxjf/article/details/8917679

A*算法,寻找最优路线的高效方法。

目录

1 STL

2 二维数组动态分配内存

3 带模板参数的模板类

4 类型转换

5 小知识点

6 函数指针

7 逗号表达式

8 运行时类型转换

9 类里面的小知识点

10 建立动态链接库

个人笔记,记录一些基础,但是又经常用到的C++方法。 不断整理ing

1、 STL

(1) vector 迭代器的使用

vector<string> title(10);// 申请一个十个string的vector容器vector<string>::iterator p;// 声明一个迭代器p= title.begin();// 这是就可以对p取值, 累加cout<<*p++<<endl<<*p<<endl;//输出 title[0],title[1]

title.push_back() 它将元素添加到矢量末尾,此时title的容积增了1个元素。

验证代码:

int main(){vector<int > pp(100);vector<int>::iterator p;int i=0;for(p= pp.begin(); p!=pp.end(); p++){pp[i]=i;i++;}cout<<(pp.end()-pp.begin())<<endl;pp.push_back(12);pp.push_back(12);cout<<(pp.end()-pp.begin())<<endl;}

输出:


title.erase(title.begin(), tetle.begin()+2);// 擦除开始的两个元素(删除的元素为    [参数1, 参数2)即不删除参数2所指向的元素)  如果第一个参数 大于第二个参数,将不做删除。 

vector 中自定义排序问题,random_shuffle 函数随机排列区间的元素, sort 按规则排序,通过自己定义函数返回bool量决定。代码中实现降序排序。

#include<iostream>#include <string>#include<vector>#include<algorithm>#include<iterator>using namespace std;void Dispaly(int p){cout<<p<<endl;}bool NewSort(int p,int q){if (p>q)return true;else return false;}void AddData(int& a){static int i=0;a=i;++i;}int main(){vector<int > pp(20);vector<int>::iterator p;//for(p= pp.begin(); p!=pp.end(); p++)//{//pp[i]=i;//i++;//}ostream_iterator<int, char> out_iter(cout,"\n");// \n为没输出一个元素后的分隔符 for_each(pp.begin(), pp.end(),AddData);random_shuffle(pp.begin(), pp.end());sort(pp.begin(), pp.end(), NewSort);//for_each(pp.begin(), pp.end(),Dispaly);copy(pp.begin(), pp.end(), out_iter);}
(2) copy此处的作用是将数据复制到到显示器中, 模板ostream_iterator是输出迭代器概念的一个模型。需要包含头文件 iterator,out_iter迭代器现在是一个接口让你可以使用cout来显示信息。 模板中第一个参数(这里为int)指出了输出的数据类型,第二个参数(char)指出了输出使用字符类型。
通样输入也可以使用输入输出迭代器。 copy(istream_iterator<int, char>(cin), istream_iterator<int,char>(), pp.begin())// 作用从输入流中读取,知道文件结尾、类型不匹配或者出现错误为止。

reverse_iterator反向迭代器(目的是代码重用),例如 rbegin() 返回一个指向超尾的反向迭代器,rend()返回一个指向第一个元素的反向迭代器。copy(pp.rbegin, pp.rend(), out_iter);可以反向显示内容。 对rbegin()递增就是将导致它递减。

vector<int>::reverse_iterater reverp;

for(reverp=pp.rbegin; reberp!= pp.rend;++ reverp)

cout<<*reverp<<"  ";

reverp= pp.rbegin(),指向超尾,rend()返回第一个元素的位置因此不能对其进行解除引用,反向指针通过先递减,再解除引用解决了这两个问题。


 *注意:rbegin(), 和end() 返回一样的超尾值,但是类型不同。

int main(){string str[4]={"1","2","3","4"};string str1[2]={"one","two"};string str2[2]={"three","four"};vector<string> word(4);copy(str, str+4, word.begin());ostream_iterator<string, char>out(cout, "  ");copy(word.begin(), word.end(), out);//back_inser_iteratorcout<<endl;copy(str1, str1+2, back_insert_iterator<vector<string>>(word));//在word 的尾部加copy(word.begin(), word.end(), out);cout<<endl;// insert_iteratorcopy(str2, str2+2,insert_iterator<vector<string>>(word,  word.begin()+2));copy(word.begin(), word.end(), out);cout<<endl<<"down!"<<endl;}

输出:



(3)AddData是通过引用传递的,所以可以修改vector中的元素,同样也可以使用注释掉的for循环来赋值。


小结: 迭代器就像指针,遍历整个容器可以使用 for(p=title.begin(); p!=title.end(); ++p。vector可以说是数据的类的表示,它提供自动内存管理功能,可以动态改变vector对象长度,并随着元素的添加和删除而增大和减小。在尾部添加和删除元素时间是固定的,但在头部或中间插入和删除元素时间复杂度为线性的。


2、 二维数组动态分配内存

int** erwei= (int**) new int[SIZE1];for(int i=0; i<SIZE1; i++){   erwei[i]= new int[SIZE2];  //*(erwei+1)= new int[SIZE2]}//释放内存for(int i=0; i<SIZE1;i++){  delete []erwei[i];}delete []erwei;


3 带模板参数的模板类

#include<iostream>using namespace std;template <class T>class Array{public:Array(){};Array( T i ):data_a( i ){}void Display(){cout<<"Arry display "<<endl;}private:T data_a;};//template<class T, template<class>class A>其中//class T 是模板B的第一个参数, 而template<class> class A//说明第二个参数是一个模板参数 也说明了类模板A是一个又一个参数的模板参数// 同样也可以写成 template<class T, template<class Q>class A>template<class T, template<class>class A>class B{public:B(){cout<<"construction of b: "<<endl;}void Display(){a.Display(); cout<<"B display: "<<endl;}private:T data_b;A<T> a;};int main(){Array<int> mya(3);B<int,  Array> b;b.Display();}



4 类型转换
假设要将一个int类型转换为double。在c标准中可以这样
int num1, num2;
double num3= ((bouble)num1)/ num2;


如果采用新的c++转换法:
double num3=static_cast<double>(num1)/num2;


而将一个const常量转换为一个变量应用const_cast.即可用const_cast去点对象的常量性。
#   dynamic_cast,可以将base class objects的pointers或references转换为 指向derived class objects的pointers 或 references.
Widget *pw;
...
update(dynamic_cast<SpecialWidget*>pw); 将pw指向的widget转换为派生类的specialwidget。


5 小知识点

#在条件判断中当c++编译器已经确定了判断的结果将不会在进行后面的判断:
例如 if( (index < lowerBoucd) || (index > upperBround)),   当第一个条件满足时, 就不会再判断index是否 >upperbround。而是直接执行条件内的代码。 
# 逗号表达式
逗号表达式的左侧会先评估,然后是逗号的右侧再被评估,最后整个逗号表达式的值是右侧的值。
例如
for(int i=0,j=20; i<20; j>0; ++i, --j); 编译器先评估++i, 再是 --j, 后面整个表达式的结果是 --j的返回值。
C++的auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。auto_ptr智能指针能在不需要对象时自动释放内存,避免内存泄露。
int* p = new int(0);auto_ptr<int> ap1(p);auto_ptr<int> ap2(p);//因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用au//to_ptr.

#应该避免异常(exception)传出析构函数(destructor)之外。
因为在析构函数中抛出的异常不能被捕捉, 所以它会传播到destructor的调用端。但是万一这个destructor本身是因为其他某个exception而被调用的,termina函数会自动被调用。 于是程序便走上了不归路(退出了)。
解决办法:
在destructor 析构函数中可能引起异常的函数或操作加上以下的代码,
~A{try{// 可能依法异常的操作}catch(...)//catch中什么也不做 就是不让异常跑出到析构函数的外面{}}

这样 terminate函数就不会调用,程序不会终止。
异常跑出的都是参数的副本,以为在函数中异常跑出时,这个函数已经失去了控制权,所以这个函数中的变量也就没有存在的意义了,因此必须使用参数的副本。
catch(Widget& w){...throw;}catch(Widget& w){...throw w;}
这两个catch的唯一区别就是,前者抛出的是当前的exception, 后者跑出的是当前exception的副本。 一般使用throw抛出当前异常原因之一就是没有复制行为,速度更快。

# 异常捕捉中部存在隐式转换, 这和函数调用时有区别的。

try{int value;if(fountion())throw value;}catch(double b){...}
try 语句中抛出的int异常是不会被double exception捕捉到的。

会转换的只有两种情况:
1) 继承架构中的类转换, 及一个针对base class编写的catch语句,可以用来处理类型为derived classs。
2)从一个“有型指针”转为“无形指针”所以一个设计为const void*指针而设计的catch子句是可以捕捉任何指针类型的exception。(catch (const void*))

所以写程序时据不要将“针对base class而设计的 catch子句”放在“针对derived class子句”之前。

注: 捕捉异常一般用引用,这样传的参数只被复制一次,效果较其他两种更高,同时还能避免一些问题(例如 不会因起切割问题)


6 函数指针

#include<iostream>using namespace std;typedef void (*CallBackPtr)(int x, int y,int  data);void display(int x, int y,int data){cout<<"x "<<x<<" y "<<y<<" data "<<data<<endl;}int main(){CallBackPtr pcall;pcall=display;//f好乐迪封建礼教pcall(2,4, 5);}

int func(int x); /* 声明一个函数 */
int (*f) (int x); /* 声明一个函数指针 */
f=func; /* 将func函数的首地址赋给指针f */
7 逗号表达式

c语言提供一种特殊的运算符,逗号运算符,优先级别最低,它将两式联接起来,如:(3+5,6+8)称为逗号表达式,其求解过程先表达式1,后表达式2,整个表达式值是表达式2的值,如:(3+5,6+8)的值是14。(a=3*5,a*4)的值是60。
逗号表达式的形式如下:
表达式1,表达式2,表达式3,...... ,表达式n
逗号表达式的要领:
(1) 逗号表达式的运算过程为:从左往右逐个计算表达式。
(2) 逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值。
(3) 逗号运算符的优先级别在所有运算符中最低。

8 运行时类型转换
代码: 多态的IsA()函数检查其参数是否与它的类型参数 id相容,如果为真则说明类型转换时有效的,并且返回匹配的类型转换指针。
#include<iostream>#include<vector>using namespace std;class Security{protected:enum{ BASEID = 0};public:virtual ~Security(){};virtual bool IsA(int id) {return id == BASEID;}};class Stock: public Security{typedef Security Super;protected:enum {OFFSET =1,TYPEID = OFFSET + BASEID};public:virtual bool IsA(int id) {return id == TYPEID || Super::IsA(id);}static Stock* dynacast(Super* s){return (s->IsA(TYPEID))? static_cast<Stock*>(s) : 0;}};class Bond: public Security{typedef Security Super;protected:enum {OFFSET =2,TYPEID = OFFSET + BASEID};public:virtual bool IsA(int id) {return id == TYPEID || Super::IsA(id);}static Bond* dynacast(Super* s){return (s->IsA(TYPEID))? static_cast<Bond*>(s) : 0;}};class Investment: public Security{typedef Security Super;protected:enum {OFFSET =3,TYPEID = OFFSET + BASEID};public:virtual bool IsA(int id) {return id == TYPEID || Super::IsA(id);}static Investment* dynacast(Super* s){return (s->IsA(TYPEID))? static_cast<Investment*>(s) : 0;}void Special(){cout<<"special investment function"<<endl;}};class Metal: public Investment{typedef Security Super;protected:enum {OFFSET =4,TYPEID = OFFSET + BASEID};public:virtual bool IsA(int id) {return id == TYPEID || Super::IsA(id);}static Metal* dynacast(Super* s){return (s->IsA(TYPEID))? static_cast<Metal*>(s) : 0;}};int main(){vector<Security*> portfolio;portfolio.push_back(new Metal);portfolio.push_back(new Investment);portfolio.push_back(new Bond);portfolio.push_back(new Stock);portfolio.push_back(new Security);for (vector<Security*>::iterator it = portfolio.begin(); it!= portfolio.end(); ++it){Investment* cm = Investment::dynacast(*it);if (cm){cm->Special();}elsecout<<"not an Investment"<<endl;}cout<<"cast from intermediate pointer:"<<endl;Security* sp =new Metal;Investment* cp = Investment::dynacast(sp);if (cp){cout<<"it is Investment"<<endl;}Metal* mp = Metal::dynacast(sp);if (mp){cout<<"is is Metal"<<endl;}cout<<typeid(sp).name()<<endl;        cout<<typeid(cp).name()<<endl;        cout<<typeid(*sp).name()<<endl;}


9 类里面的小知识点



这样可以避免一次初始化 一次赋值, 提高效率。 但是对内建类型则没有用处, 例如int float 类型是没有构造函数的,不过都以初始化列表可以是代码格式更加统一。
#初始化的顺序是成员函数在类中声明的顺序。

10 建立动态链接库
首先建立一个win32的动态链接库工程,添加实现文件(cpp)编写函数后将函数导出。要从动态链接库中导出函数使用 在函数前加 _declspec(dllexport)
 查看动态链接库中导出的函数, 可以在命令行中先到工程目录, 使用vc提供的,dumpbin XX.dll 来查看
#要查看一个动态链接库导出的函数, 在命令行红利用 cd命令先进入到dll的文件目录中,再输入 dumpbin -exports XX.dll查看。
#查看一个可执行文件的输入信息,先进入到可执行文件的目录想输入  dumpbin -imports XX.exe即可。
#当在工程中要使用动态链接库中的函数时, 有两种方法
//extern int add(int a, int b);//extern int substract(int a, int b);_declspec(dllimport) int add(int a , int b);_declspec(dllimport) int substract(int a, int b);
用extern声明表明其是在外部定义的函数, 而利用_declspec(dllimport) 表明其在动态链接库中, 这时编译器可以生产更加高效的代码,所以一般使用_declspec(dllimport)来导入函数。

同样我们还可以在自己建 的dll工程中添加一个头文件将这些函数导入以供用户使用。加入实现稳健为mydll.cpp, 则添加头文件在并添加cpp文件中导出的函数。
dll.h:
_declspec(dllimport) int add(int a, int b);_declspec(dllimport) int substract(int a, int b);
import表明函数是从动态链接库中导入的。

最后在要使用dll函数的工程中添加这个头文件即可。 例如:#include“目录\dll.h”,这样就可以使用导出的函数了

#类的导出, 类的成员函数导出
.h
//mydll.h#ifdef MYDLL_API#else#define MYDLL_API _declspec(dllimport)#endifMYDLL_API int add(int a, int b);MYDLL_API int substract(int a, int b);//其中 若将注释去掉,则说明导出的是整个类 class /*MYDLL_API*/ Point{private:int m_a;int m_b;public:Point(int a=0, int b=0):m_a(a), m_b(b){};MYDLL_API void OutPut() const;};

.cpp
#define MYDLL_API _declspec(dllexport)#include"mydll.h"#include<iostream>int add(int a, int b){return a+b;}int substract(int a, int b){return a-b;}void Point::OutPut()const{using std::cout;using std::endl;cout<<"m_a "<< m_a<<",m_b  "<<m_b<<endl;}
extern "C" _declspec(dllexport) 加入 extern“C”后是一C标准导出函数,即函数名不发生改变。 add导出的函数名还是 add,但是这个方法只能用于导出全局函数,不能用于导出类的成员函数。
#自定义导出函数的格式,可以在工程中新建一个txt文档,将后缀改为.def,文件名改成工程文件名(不是必需的)。在.def文件中添加要导出函数的名字
实例:
LIBRARY MyDll2
EXPORTS
add @1
substract @2
display @3

首先给工程配置好:以vs2010为标准, 首先工程属性-》连接器-》输入-》 模块定义文件   中输入你的.def文件名,加上后缀。
add 为要导出的函数, 后面的@1说明导出的序号。 可以通过这两种方式调用函数,后面将会说明。
如果写成: myadd = add,则dll中的 add函数的导出名字将会变成myadd。
下面是两种不同的方式调用函数, 采用动态链接方式节省资源。
#include<iostream>#include<Windows.h>//#include"..\..\MyDll\MyDll\mydll.h"//#pragma comment( lib, "..\\debug\\libTest.lib" )  //指定与静态库一起连接using namespace std;//extern int add(int a, int b);//extern int substract(int a, int b);//_declspec(dllimport) int add(int a , int b);//_declspec(dllimport) int substract(int a, int b);int main(){HINSTANCE hInst;hInst = LoadLibrary("MyDll2.dll");typedef int (*ADDPROC)(int a, int b);ADDPROC Add = (ADDPROC)GetProcAddress(hInst, MAKEINTRESOURCE(1));ADDPROC Substract = (ADDPROC)GetProcAddress(hInst, "substract");int sum= Add(2,4);int sub = Substract( 4, 3);cout<<sum<<endl<<sub<<endl;FreeLibrary(hInst);}
用完后最后释放动态链接库FreeLibrary(hInst);