C++实习笔记(4)
1.## 读取文件的路径问题 ##
项目中,SDK初始化时需要从写好的ini配置文件中读取参数值,这就涉及到了从磁盘中读取文件。
当用相对路径从磁盘中读取文件时,如果调试提示找不到文件时,最好先尝试一下绝对路径,看是否是路径有问题。
首先需要看启动项目的属性->调试->工作目录,看看此时的工作目录是(ProjectDir)还是(OutDir):
$$(ProjectDir)指的是工程文件(.vcproj)所在目录路径。
$(OutDir)指的是生成的文件所在的文件夹(一般是bin下)路径。
因为此时工作目录设置为(OutDir),所以将需要的ini文件放在USERSDK_sln\bin\x64\P3_Release_CPU路径下。
注意此时相对路径指的就是目标文件相对(OutDir)文件路径下的路径。也即是说(OutDir)文件路径是起点,从此处到目标文件位置的路径。(ini文件所在位置)
因为之前我们将ini文件放在了USERSDK_sln\bin\x64\P3_Release_CPU文件夹下,所以此时需要从$(OutDir)文件夹下寻找ini文件。也就是说:
起点位置:$(OutDir) —– USERSDK_sln\bin\x64\P3_Release_CPU
ini文件所在位置:USERSDK_sln\bin\x64\P3_Release_CPU\GearCfg.ini
也就是从USERSDK_sln\bin\x64\P3_Release_CPU\GearCfg.ini返回到USERSDK_sln\bin\x64\P3_Release_CPU。
注意此时需要回退几次到达目标位置,则相对路径设置为有几次“../”
例如此时,只需要往前返回一次即可到达目标位置,则此时的相对路径可以设置为:
"../P3_Release_CPU"则表示此时路径起点位置是USERSDK_sln\bin\x64\P3_Release_CPU,完整读取代码为:
std::string cfg = std::string(szPath); //szPath是传进来的参数 == "../P3_Release_CPU"
cfg.append("/GearCfg.ini"); //表示在cfg后面加上"/GearCfg.ini"
if (m_cfg.LoadEx(cfg.c_str()) != 0)
{
SDK_LOG_INFO("can not load GearCfg.ini folder = %s wfolder = %ls", cfg.c_str(), szPath);
return RetError(-1, "can not load GearCfg.ini", EM_INIT);
}2.函数返回值问题
对于常见的有返回值类型的函数,例如int型,即使其正常运行完所有代码,一定不能忘记在成功执行函数后返回0,
...
return 0;因为如果int类型的函数最后执行成功了没有return,在有些编译器中会报错,亲测Visual Studio2013中会提示:不是所有的控件路径都有返回值。则可能此时编译器会随机返回一个数,造成错误。
3.指针的内存分配问题
今天在使用memcpy复制图像的时候,不小心犯了个严重错误。。惭愧。
...
unsigned char* c_image;
memcpy(c_image.get(),buf.get(),size);
LOGI("GearVrApiJni::InputCameraImage2");注意此时unsigned char* c_image只是定义了一个char*指针,并没有给它分配内存空间,所以是不能进行memcpy的。如果此时定义的是char c_iamge[100],那么编译器会在栈上为数组变量分配100字节的内存空间,此时是可以直接memcpy(c_image.get(),buf.get(),size); 的。
但是如果定义为char* 的话,编译器只会在栈上为指针变量分配sizeof(char )的大小内存(用来保存指针本身),是不能被memcpy的。所以此时则需要先将指针分配内存空间,也即是指向有效的内存地址。对于char则可以为它动态在堆上分配内存,或者指向另外一个字符串,例如null。NULL表示的地址是0*00000000 也就是空,实际上,NULL就等于0,只不过用于指针时,通常用NULL,便于阅读。
1.传统的malloc分配内存方法:
首先malloc函数是在stdlib.h这个头文件里面的,所以需要加上这个头文件。
int *a;
a = (int *)malloc(20 * sizeof(int)); //分配内存
a[0] = 1;
a[1] = 2;
printf("%d %d\n", a[0], a[1]);
free(a);这是用malloc代码达到了new[]的效果,申请出一段连续的内存(20个int),所需要内存的大小由我们自己控制,把握好别越界即可。且需要注意的是malloc函数返回的是void*类型,对于C++必须通过强制转换来使用,例如(int *)malloc()。
但是需要注意的是malloc函数只能分配内存,并不能对其所得到的内存进行初始化,所以得到的一片新内存中,其值将是随机的。
另外,对于不需要的内存,一定要通过free()来释放。
2.C++中的new分配内存方法:
首先,new并不是一个函数,而是一个关键字,同时也是操作符。当使用new关键字在堆上动态创建一个对象时,实际上做了三件事情:
1.获得一块内存空间
2.调用构造函数
3.返回正确的指针
当然,如果创建的是简单类型的变量,那么第二步将会被省略。
例如有一个类:
class A
{
int i;
public:
A(int _i) :i(_i*_i) {}
void Say() { printf("i=%d/n", i); }
};当调用new的时候:
A *pa = new A(3);
那么实际上动态创建一个对象的过程大致相当于一下三句话(只是大致相当,并不一样!!!)
A* pa = (A*)malloc(sizeof(A));
pa->A::A(3);
return pa;上述三句话也得到了一个有效的指向堆上的A对象的指针pa,但区别在于当malloc失败时,它不会调用分配内存失败的处理程序new_handler,但是new的话则会。因此,尽量还是使用new比较好。
注意,new其实有三种形式,这只是一般我们常用的new operator 形式,其余形式请看:
new的三种形式
3.C++11的std::shared智能指针方法
智能指针的原理是,接受一个申请好的内存地址(new,malloc),构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈上的变量自动被销毁,智能指针内部保存的内存也就被释放掉了。
智能指针在C++11版本之后提供了三种smart pointer,包含在头文件中,分别是shared_ptr,unique_ptr,weak_ptr。
shared_ptr是多个指针指向相同的对象,其使用引用计数,每一个shared_ptr的拷贝都指向相同的内存,每使用它一次,内部的引用计数+1,每析构一次,内部的引用计数-1,当计数减为0时,自动删除其所指向的对内存。且shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
3.1 shared_str初始化
shared_str可以通过构造函数,std::make_shared,以及reset方法来初始化。
#include "stdafx.h"
#include <iostream>
#include <future>
#include <thread>
using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}
int value;
};
int main()
{
std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用计数为1
std::shared_ptr<Person> p2 = std::make_shared<Person>(2);
//通过make_shared赋值,其作用和构造函数赋值一样。
p1.reset(new Person(3));
// 通过reset赋值。首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
// 最后将新对象的指针交给智能指针
std::shared_ptr<Person> p3 = p1;
//现在p1和p3同时指向Person(3),Person(3)的引用计数为2
p1.reset();//Person(3)的引用计数为1
p3.reset();//Person(3)的引用计数为0,析构Person(3)
return 0;
**std::shared_ptr<int> p4 = new int(1);**
//Error!!!不能将一个原始指针直接赋值给一个智能指针(可以通过构造函数赋值)
//因为智能指针是一个类,原始指针是一个指针
std::shared_ptr<int> p4(new int(5));
int *pInt = p4.get();
//但是可以通过.get()来获取原始指针
int *p5 = new int;
std::shared_ptr<int> p6(p5);
std::shared_ptr<int> p7(p5);// logic error
//拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1
//不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存
//此时给p6赋值使得原对象引用计数减为0,释放p5内存;在给p7赋值使得原对象引用计数减为0,
//再次释放p5内存,二次释放。
----------
#include <iostream>
#include <memory>
int main() {
{
int a = 10;
std::shared_ptr<int> ptra = std::make_shared<int>(a);
std::shared_ptr<int> ptra2(ptra); //copy,引用计数+1
std::cout << ptra.use_count() << std::endl;
int b = 20;
int *pb = &a;
//std::shared_ptr<int> ptrb = pb; error,不能直接赋值
std::shared_ptr<int> ptrb = std::make_shared<int>(b);
//等价于std::shared_ptr<int> ptrb(b);
ptra2 = ptrb; //assign
pb = ptrb.get(); //获取原始指针
std::cout << ptra.use_count() << std::endl;
std::cout << ptrb.use_count() << std::endl;
}
}
}在这里,我们使用智能指针管理申请的new unsigned char内存,同时此时自定义了智能指针的释放函数。
std::shared_ptr<jbyte> buf(env->GetByteArrayElements(image, nullptr), [=](jbyte *p) {
env->ReleaseByteArrayElements(image, p, JNI_ABORT);
});
std::shared_ptr<unsigned char> c_image(new unsigned char[size],[](unsigned char*p){if(p) delete [] p; p = nullptr;});
//创建智能指针,同时自定义释放函数
memcpy(c_image.get(),buf.get(),size);
LOGI("GearVrApiJni::InputCameraImage2");