C++实习笔记(4)

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");

版权声明:本文为gzrjzcx原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。