C++个人笔记(一)

总结:

另有一篇文章 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_iterator
	cout<<endl;
	copy(str1, str1+2, back_insert_iterator<vector<string>>(word));//在word 的尾部加
	copy(word.begin(), word.end(), out);
	cout<<endl;
	// insert_iterator
	copy(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();
		}
		else
			cout<<"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)
#endif
MYDLL_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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值