windows安装anaconda(傻瓜式安装),可以勾选一个自动设置环境变量的东西,省事
Anaconda3 x64 (with Python 3.5/3.6)Windows 64位系统(Windows 7 或 Windows Server 2008 及以上)CUDA 8 / CUDA 9cuDNN v5以上如果安装了CUDA编译的包,请确保你的电脑有Nvidia的显卡。注:这里没有介绍GPU版本的安装方法,如需要的请搜索其他博文。
开始菜单打开Anaconda Prompt(在ubuntu下,直接打开命令终端),在里面输入conda create -n pytorch python=3.6,
(根据python版本号设置,版本号可以通过python指令查看)为pytorch创建一个虚拟环境
添加清华镜像(非必需,当时没添加镜像也成功了,只是网络可能会波动,多试几次,另外下载的时候,最好不要进行任何操作)conda config --addchannels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --setshow_channel_urls yes
科大镜像(清华已经关闭镜像):
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/conda config --set show_channel_urls yes##现在科大镜像也关闭了,不知何时能开启##(想要删除清华源把add改成remove就行。)
腾讯的还可以试试:
https://mirrors.cloud.tencent.com/anaconda/pkgs/free/
打开anaconda navigator,点击environment,可以看到base基础环境,以及刚刚新建的pytorch环境
,
使用的是ocv3.30,vs20173
2 此电脑>右键->属性->高级系统设置->环境变量
找到系统变量中的path变量,双击它,点击新建,将你解压的opencv文件夹中的***opencv\build\x64\vc14\bin路径添加到当中
3 网上很多教程没有这一步,会报那种找不到dll文件的错误,是因为没有将opencv里面的相关文件复制到C盘中的文件夹里面
操作方法:将bin目录下面的opencv_world330.dll和opencv_world330d.dll文件复制到C:\Windows\SysWOW64这个文件夹里面即可;将bin目录里面的opencv_ffmpeg330_64.dll复制到C:\Windows\System32这个文件夹里面(详细看图)
如果是opencv其他的版本,把对应的dll文件移动到上述两个C盘文件夹即可!
D:\ opencv \opencv\build\includeD:\ opencv \opencv\build\include\opencvD:\ opencv \opencv\build\include\opencv2
库目录中加入
D:\ opencv \opencv\build\x64\vc14\lib
opencv_world330d.lib
注意:到上面所有工作,opencv已经配完了,注意解决方案平台那一栏要换成X64(因为我们一直都在配X64)
测试代码:
#include <io
对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态内存分配解决了这个问题。
new和delete运算符是用于动态分配和撤销内存的运算符。
使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。
new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。
一般使用格式:
格式1:指针变量名=new 类型标识符;格式2:指针变量名=new 类型标识符(初始值);格式3:指针变量名=new 类型标识符 [内存单元个数];
说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。例如:
1)new int; //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a2)int *a = new int(5) 作用同上,但是同时将整数空间赋值为5
对于数组进行动态分配的格式为:
指针变量名=new 类型名[下标表达式];delete [ ] 指向该数组的指针变量名;
两式中的方括号是非常重要的,两者必须配对使用,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。
delete []的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。
众所周知,最开始我们用new来创建一个指针,那么等我们用完它之后,一定要用delete将该指针删掉。但是,值得注意的是,难道就仅仅是删除这个指针这么简单的么?下面,我们用一个程序来说明这个问题:
#include <iostream>using namespace std;int main(){int *p=new int;*p=3;cout<<"将3赋给p的地址后,指针p读取的值:"<<*p<<endl;delete p;cout<<"删除空间后,指针p读取的值:"<<*p<<endl;long *p1=new long;*p1=100;cout<<"创建新空间后,指针p中保存的地址:"<<p<<endl;cout<<"指向新空间的指针p1保存的地址:"<<p1<<endl;*p=23;cout<<"将23赋给p的地址后,指针p读取的值:"<<*p<<endl;cout<<"将23赋给p的地址后,指针p1读取的值:"<<*p1<<endl;delete p1;return 0;}
在上面这个程序中,我们在第8行就将指针p利用delete删掉了。但是,我们来看看程序的输出结果:
对照着上面的程序,我们来分析一下这个输出。首先,我们在程序的第5行初始化了一个指针p。之后输出指针p读取的值。由于第6行的原因,程序肯定会输出3了。之后,我们在程序的第8行删除了这个指针p。但是我们惊奇的发现,在程序的第9行竟然可以输出指针p读取的值。我们不是已经把它删了么?其实不然,debug,上图:
从监视窗口中,我们可以看见虽然程序的第8行已经将指针p删除了,但是在监视窗口中p仍然存在,只是*p所指向的值不再是原来的3了,而是一个随机数。这里就说明了一个非常重要的概念:我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身。
然后我们接着往下分析。在程序的第10行我们又创建了一个long型的指针p1。在12行与13行的输出中我们惊奇地发现,指针p保存的地址居然和指针p
为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned
string::size_type 制类型一般就是unsigned int, 但是不同机器环境长度可能不同 win32 和win64上长度差别;size_type一般也是unsigned int
使用的时候可以参考:
string::size_type a =123;vector<int>::size_type b=234;size_t b=456;
size_t 使用的时候头文件需要 (vs2017中貌似不需要, std::size_t sz;即可声明变量size_t类型变量sz) ;size_type 使用的时候需要或者
sizeof(string::size_type)sizeof(vector<bool>::size_type)sizeof(vector<char>::size_type)sizeof(size_t)
上述长度均相等,长度为win32:4 win64:8
6. 二者联系:在用下标访问元素时,vector使用vector::size_type作为下标类型,而数组下标的正确类型则是size_t
By lb 2018/8/24
//自定义拷贝构造函数classname(const classname &ob){//自定义拷贝构造函数的函数体}// 其中ob是用来初始另一个对象的对象的引用
class StringData{private: char *str;public:StringData(char *s){str=new char[strlen(s)+1];strcpy(str,s);}StringData(const StringData &p){str=new char[strlen(p.str)+1];strcpy(str,p.str);}~StringData() { delete str; }//…};int main(){StringData x(“abc”);StringData y(x);getchar();return 0;}
说明:
拷贝构造函数是一种特殊的构造函数。它用于依据已存在的对象建立一个新对象。
如果一个对象里面有指针成员,并且这个指针已经动态分配空间了,同时,对象有析构函数对这个指针进行释放。如上面那个例子,如果我们通过这个对象创建一个新对象:
A a("123");A b = a; // 调用拷贝构造函数
如果我们没有自定义拷贝构造函数,导致对象 a 和 b 的指针成员指向同一个地址空间,当对象生命周期结束时,a 和 b 都会调用析构函数,最后导致,这个指针会被释放 2 次,导致内存出问题。
引用
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
所以,对象有指针成员,尽量自定义拷贝构造函数。
先看一个例子:
#include <iostream>using namespace std;class A{public:A() {show();}virtual void show(){cout<<"in A"<<endl;}virtual ~A(){show();}};class B:public A{public:B() {show();}void show(){cout<<"in B"<<endl;}};int main(){A *a = new A;//输出"in A"delete a;//输出"in A"cout << "*******我是分割线**********" << endl;A *b = new B;//输出"in A"后,输出"in B"delete b;//输出"in B"后,再输出"in A"getchar();return 0;}
输出结果,可以看到没有预想的多态效果:
in Ain A*******我是分割线**********in Ain Bin Bin A
结论:构造函数和析构函数调用虚函数时都不使用动态联编,如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。
原因分析:
(1)不要在构造函数中调用虚函数的原因:因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化, 因此调用子类的虚函数是不安全的,故而C++不会进行动态联编。
(2)不要在析构函数中调用虚函数的原因:析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经“销毁”,这个时再调用子类的虚函数已经没有意义了。
int *a,*b;//声明两个指针变量a=b; //指针复制:此语句使a指向的地址与b指向的地址相同,它们都指向同一内存区域。*a=*b ; //指针赋值:此语句使a指向的地址(内存位置)的内容与b指向地址(内存位置)的内容相同,但a与b指向的地址不一定相同
a,b,都是指针变量,*a表示指针变量所指向的值,同样*b也是如此。*a=*b就是表示把指针b所指向的值赋值给指针变量a所指向的值。
但是要是指针a要是没有初始化或是开辟空间的话,这样赋值就有危险!!!
例如:
int *a;int *b=5;*a=*b;
首先声明一个指针变量a,但是系统并没有为其分配空间,也就是说此时指针变量的值即地址不确定的,那么*a也就表示一个不确定的值,有可能这个值是系统的重要数据,那么*a=*b;不就修改了系统的数据了吗???也就变量的危险了。
所以声明指针变量的时候一定注意初始化,初始化为0也可以,代表空指针。
4
&a=&b ; //这个一般比较少用,使指针a本身的内容与指针b本身的内容相同
5
总结指针的用处:用于指向与其类型相同的地址,重点在于指向地址,而不在于指针本身的内容(故&a=&b一般仅作理解,比较少用)
一句话:指针a是一个地址, *a是它指向的内容,&a是它本身的内容
必须保证基类和派生类中对应的虚函数除了函数名称一致之外,还必须保证形参和返回值也完全一致!
若基类和派生类中仅仅是虚函数的函数名称一致,但是形参不一致,则无法实现动态绑定/多态!实际上造成的结果是:派生类中对基类中对应的函数进行了隐藏,在派生类中只能访问派生类中的该函数,在基类中只能访问基类中对应的同名函数。
针对基类和派生类中的虚函数有没有默认实参问题进行讨论。
#include<iostream>using namespace std;class super{public:virtual void somemethod()const { cout<<"base" << endl; }};class sub : public super{public:virtual void somemethod(int i = 12)const { cout<<"sub" << "i = " << i <<endl; }};int main(void){sub mysub;super &ref = mysub;ref.somemethod(); // 调用哪个?return 0;}
输出:
base
调用的是基类的,派生类是基类这个函数的重新定义的版本,无法实现动态绑定/多态。(这个务必引起重视,因为形参已经不一样了)
#include<iostream>using namespace std;class super{public:virtual void somemethod(int i = 21)const {cout<<"base"<<"i = "<< i <<endl;}};class sub : public super{public:virtual void somemethod()const { cout<<"sub" << "i = " << i <<e