C++进阶(一)

目录

 

1、std::bind()使用

2、命名空间

3、inline函数

4、选择SelectViews

5、typedef

 6、ASSERT

7、tbb 如何限制只有同一时间可以写入数据呢

8、结构体命名冲突

9、进度条回调函数

10、动态库


1、std::bind()使用

基本上就是奇怪的调用方法,看离职人写的,无语。

`std::bind()` 是C++11中引入的一个功能,用于产生绑定到特定参数的函数对象。基本上,它允许你固定或"绑定"一个或多个参数到特定的函数,并返回一个新的可调用对象。当这个新的可调用对象被调用时,它将使用预先绑定的参数调用原始函数。

这是一些基本的使用`std::bind()`的例子:

1. **绑定非静态成员函数**:

    ```cpp
    #include <functional>
    #include <iostream>

    class MyClass {
    public:
        void printNumber(int x) {
            std::cout << "Number: " << x << std::endl;
        }
    };

    int main() {
        MyClass obj;
        auto boundMemberFn = std::bind(&MyClass::printNumber, &obj, 42);
        boundMemberFn();  // 输出: Number: 42
    }
    ```

2. **使用占位符**:

    `std::placeholders`命名空间下有一些特殊的占位符,它们可以在绑定时使用,表示当生成的可调用对象被调用时应该传递给函数的参数。

    ```cpp
    #include <functional>
    #include <iostream>

    void printSum(int a, int b) {
        std::cout << a + b << std::endl;
    }

    int main() {
        using namespace std::placeholders;  // 为了使用 _1, _2 等

        auto boundFn = std::bind(printSum, _1, 42);
        boundFn(10);  // 输出: 52
    }
    ```

    在上面的例子中,我们将`printSum`的第二个参数绑定为42,并使第一个参数保持开放,这样我们可以稍后提供它。

3. **将成员变量作为getter**:

    ```cpp
    #include <functional>
    #include <iostream>

    class MyClass {
    public:
        int value = 42;
    };

    int main() {
        MyClass obj;
        auto boundGetter = std::bind(&MyClass::value, &obj);
        std::cout << boundGetter() << std::endl;  // 输出: 42
    }
    ```

尽管`std::bind()`是一个功能强大的工具,但在许多情况下,使用C++11的lambda表达式更为简洁和直观。

2、命名空间

namespace MVS {
class MVS_API CameraIntern
{
public:
}

} 这是一个.h的c++文件。我不清楚这种写法啥意思,有什么用。其他地方该如何调用呢。我一般只会使用calss   osgb2obj
{
public:
}  这种类似的格式

这个代码片段展示了一个C++的命名空间和类的声明,其中涉及到了一些高级特性。让我逐步解释给你:

1. **命名空间 (Namespace)**:
   - 在C++中,命名空间是用于封装变量、函数和类的一种方式,以防止命名冲突。例如,两个库都有一个名为`Camera`的类。为了避免混淆,你可以将它们放在不同的命名空间中。
   - 在这里,你看到的`namespace MVS { ... }`表示内部的所有声明都在`MVS`命名空间内。

2. **类前缀**:
   - `MVS_API`可能是一个宏定义,用于控制类的导入/导出,特别是在Windows的动态链接库(DLL)中。当你构建一个库时,这个宏可能会扩展为`__declspec(dllexport)`,表示你想将这个类导出到DLL。当你使用库时,它可能会扩展为`__declspec(dllimport)`,表示你想从DLL导入这个类。

3. **类的定义**:
   - `class CameraIntern { ... };`这是`CameraIntern`类的声明。它目前是空的,没有任何成员变量或成员函数。

如何在其他地方调用:

1. **包含头文件**:
   - 假设这个声明在`camera.h`中。在另一个文件中,你需要包含这个头文件:`#include "camera.h"`

2. **使用命名空间**:
   - 要在其他文件中使用`CameraIntern`类,你需要指定它的命名空间,如`MVS::CameraIntern camera;`
   - 或者,你可以使用`using`声明,如`using namespace MVS;`,然后直接使用`CameraIntern camera;`

3. **创建类的对象**:
   - 声明一个该类的对象:`MVS::CameraIntern myCamera;`

你的熟悉的写法是基本的类声明,而这个代码片段展示的是在库开发中更高级的用法,特别是在跨平台或大型项目中。希望这解释清楚了你的疑问!

3、inline函数

public:
	inline CameraIntern() {}
	inline CameraIntern(const Matrix3x3& _R, const Point3& _C) : R(_R), C(_C) {}
	inline CameraIntern(const Matrix3x3& _K, const Matrix3x3& _R, const Point3& _C) : K(_K), R(_R), C(_C) {}

	inline void SetT(const CMatrix& T) { C = R.t()*(-T); }
	inline CMatrix GetT() const { return R*(-C); }  
为什么用inline 这个前缀呢,不清楚。我一般不用
`inline`是C++中的一个关键字,用于建议编译器将函数的调用替换为函数本身的代码。换句话说,它是告诉编译器“内联”该函数。使用`inline`有以下目的:

1. **减少函数调用开销**:当函数被内联,函数调用的开销就被消除了。这对于那些很小、经常被调用的函数特别有用,因为函数调用的开销可能会相对于函数的实际工作变得显著。

2. **优化的机会**:当函数被内联时,编译器有更多的机会进行优化,因为它可以看到函数的实际代码和它在哪里被调用。

3. **头文件中的函数定义**:当函数定义在类声明内部或在头文件中时(如模板函数),它们通常是隐式地内联的。但是,明确地使用`inline`关键字可以使意图更清晰,并确保在多个源文件中包含相同的头文件时不会出现链接错误。

然而,使用`inline`也有一些注意事项:

- `inline`只是一个建议,编译器可以选择忽略它。换句话说,即使你标记了一个函数为`inline`,编译器也可能决定不内联它,特别是如果函数很大或复杂。

- 过度内联可能会导致代码膨胀,因为每个函数调用都被替换为函数本身的代码。这可能会使得生成的程序体积增大,并可能影响缓存效率。

在你提供的代码中,`inline`被用于构造函数和简单的成员函数,这是很常见的,因为这些函数通常很小,并且经常被调用。通过内联它们,可以提高性能。但是,总是要根据实际的使用情况和性能需求来决定是否使用`inline`。

4、选择SelectViews

bool DepthMapsData::SelectViews(IIndexArr& images, IIndexArr& imagesMap, IIndexArr& neighborsMap)
{
	// find all pair of images valid for dense reconstruction
	typedef std::unordered_map<uint64_t,float> PairAreaMap;
	PairAreaMap edges;
	double totScore(0);
	unsigned numScores(0);
	FOREACH(i, images) {
		const IIndex idx(images[i]);
		ASSERT(imagesMap[idx] != NO_ID);
		const ViewScoreArr& neighbors(arrDepthData[idx].neighbors);
		ASSERT(neighbors.GetSize() <= OPTDENSE::nMaxViews);
		// register edges
		FOREACHPTR(pNeighbor, neighbors) {
			const IIndex idx2(pNeighbor->idx.ID);
			ASSERT(imagesMap[idx2] != NO_ID);
			edges[MakePairIdx(idx,idx2)] = pNeighbor->idx.area;
			totScore += pNeighbor->score;
			++numScores;
		}
	} 这段代码看的很难受,我平时写代码感觉没那么复杂,我该如何去理解
这段代码包含了一些高级C++特性和项目特定的宏/类型。为了理解这段代码,我们可以逐步拆解它。首先,我会给出一个高级的描述,然后我们再深入每个部分。

**概览**:
该函数的目的是选择一组视图(图片)作为深度映射数据的一部分,并确定这些视图之间的关系。具体来说,它似乎正在计算哪些图片对(成对)对于密集重建是有效的。

现在,我们详细看下代码:

1. **数据类型和容器声明**:
   - `std::unordered_map<uint64_t,float> PairAreaMap`: 这定义了一个映射,其中键是`uint64_t`类型,值是`float`。这可能表示两个视图之间的关系,其中`uint64_t`是由两个图像索引组合成的,`float`可能代表了这两个图像之间的某种“面积”或关联度。
   
2. **初始化和准备**:
   - 三个变量`edges`, `totScore`, 和`numScores`被初始化。其中,`edges`映射存储了每对图片之间的关系,而其他两个变量用于累积评分。

3. **遍历所有图片**:
   - `FOREACH(i, images)`: 这是一个宏,它的行为类似于for-each循环。它遍历`images`列表。
   
   - `const IIndex idx(images[i])`和`const ViewScoreArr& neighbors(arrDepthData[idx].neighbors)`: 这两行从当前图像索引检索信息。

4. **注册边**:
   - `FOREACHPTR(pNeighbor, neighbors)`: 另一个宏,遍历`neighbors`列表。对于当前图片的每一个邻居,它检索它们之间的关系和评分,并将它们添加到`edges`映射中。

总结:这段代码的主要目的是从`images`数组中收集所有成对的图像关系,并将这些关系存储在`edges`映射中。同时,它还累计了这些关系的总评分。

要更好地理解这段代码,你可能需要查看其他部分的代码和文档,特别是与`arrDepthData`, `ViewScoreArr`和`MakePairIdx`相关的部分。不过,希望这个解释为你提供了一个合理的起点!

5、typedef

typedef std::unordered_map<uint64_t,float> PairAreaMap; 为什么前面加一个typedeft 不理解

typedef是C++中的一个关键字,它允许你为一个已经存在的数据类型定义一个新的名称。这在几种情况下都很有用:

  1. 简化复杂的类型:如你的示例所示,std::unordered_map<uint64_t,float>是一个相当复杂的数据类型。通过使用typedef,你可以为其定义一个更简单的名称,如PairAreaMap。这使得后续的代码更加简洁和可读。

 6、ASSERT

用这个干啥的


ASSERT是一个常用的宏,用于在调试时检查某个条件是否为真。
如果该条件为假,程序通常会中止执行,并提供有关违反的断言和其位置的信息。
这种机制允许开发者在开发和测试阶段迅速发现和修复错误。

int Divide(int numerator, int denominator) {
    ASSERT(denominator != 0);  // Ensure that we're not dividing by zero
    return numerator / denominator;
}

7、tbb 如何限制只有同一时间可以写入数据呢

在TBB (Threading Building Blocks) 中,如果您想确保在任何给定时间只有一个线程可以写入数据,您可以使用`tbb::spin_mutex`或`tbb::mutex`来实现这个目标。它们都是互斥锁,可以确保对共享资源的独占访问。

1. **使用`tbb::spin_mutex`**:
   
`spin_mutex`在等待锁定时会持续地轮询,这在预期等待时间短时效果较好。

```cpp
#include <tbb/tbb.h>

tbb::spin_mutex myMutex;

void writeData() {
    tbb::spin_mutex::scoped_lock lock(myMutex);
    // ... 执行写入操作 ...
}
```

2. **使用`tbb::mutex`**:

`tbb::mutex`相对于`spin_mutex`更加重量级,但在等待时间可能较长的情况下效果更好。

```cpp
#include <tbb/tbb.h>

tbb::mutex myMutex;

void writeData() {
    tbb::mutex::scoped_lock lock(myMutex);
    // ... 执行写入操作 ...
}
```

无论选择哪种方法,关键思路都是使用锁来确保在同一时间只有一个线程可以访问和修改数据。当一个线程拥有锁时,其他尝试获取锁的线程将被阻塞,直到锁被释放。

请注意,虽然使用互斥锁可以确保线程安全,但过度使用可能会导致性能下降。因此,请确保仅在确实需要保护共享资源时使用互斥锁,并尽量减少锁定的时间长度。

8、结构体命名冲突

c++,一个.h文件。struct Point3f{} 定义了。然后里面的方法调用Point3f显示 “Point3f”不明确。因为我知道vcglib也有结构叫Point3f 。我该如何是好

如果你在一个`.h`文件中定义了一个`struct Point3f`,但在使用它时遇到“不明确”的错误,这可能是因为另一个库或名称空间(例如`vcglib`)中也有一个同名的结构。这样的名称冲突可能导致编译器无法确定你引用的是哪个`Point3f`。

为了解决这个问题,你有以下几种策略:

1. **使用名称空间**:
   - 把你自己定义的`Point3f`放在一个专门的名称空间中。这样,在使用你自己的`Point3f`时,你可以通过这个名称空间来引用它,从而避免任何冲突。

```cpp
namespace MyNamespace {
    struct Point3f {
        // ... 成员和方法 ...
    };
}
```

使用时:

```cpp
MyNamespace::Point3f myPoint;
```

2. **更改名称**:
   - 可以考虑为你自己的`Point3f`结构选择一个不同的名称,例如`MyPoint3f`,以避免名称冲突。

```cpp
struct MyPoint3f {
    // ... 成员和方法 ...
};
```

3. **使用`using`声明**:
   - 如果在某个特定的源文件中,你确定要使用你自己的`Point3f`并且不使用`vcglib`的`Point3f`,你可以使用`using`声明来使你自己的`Point3f`在该文件中可用。

```cpp
using MyNamespace::Point3f;
```

然后,你可以在该文件中直接使用`Point3f`来引用你自己的定义。

4. **完全限定名称**:
   - 如果只是偶尔出现冲突,你也可以使用完全限定的名称来明确你要使用的版本,如`MyNamespace::Point3f`或`vcglib::Point3f`。

以上方法可以帮助你解决名称冲突的问题。但是,长远来看,最佳做法通常是使用名称空间,这样可以清晰地组织和隔离代码,防止未来可能的名称冲突。

9、进度条回调函数

convertTools.cpp
这个文件调用osgb2obj的一些执行方法
例如
convertMain(std::string strInput,std::string stdOutDir){
OSGB2OBJEntity osgb2objentity;
osgb2objentity.folder2obj(strInput,strOutDir);
}

osgb2obj.h
里面关于osgb2obj的一些方法

osgb2obj.cpp
里面是关于osgb2obj的具体实现
其中
for(int i=0;i<osgb.size();++i){
   我需要实时返回这个i的数据给界面上面,实时返回处理的第几个
}


我还有一个globle.h这个文件,是放置全局文件的。因为的格式转换不仅有osgb2obj,还有obj2osgb等等

这几个文件是放在一个工程中的,我需要打包成一个静态库。如何让外界调用convertMain 然后实时获得处理的第几个
为了允许外部界面能够实时获得处理的状态,您可以使用回调函数或者观察者模式。以下是使用回调函数策略来实现这一功能的方法:

1. **定义回调函数类型**

在您的`globle.h`文件中,定义一个回调函数类型:

```cpp
#include <functional>

using ProgressCallback = std::function<void(int current, int total)>;
```

2. **为`OSGB2OBJEntity`添加回调功能**

在`osgb2obj.h`中,修改`OSGB2OBJEntity`类,使其能够接收并存储回调函数:

```cpp
#include "globle.h"

class OSGB2OBJEntity {
public:
    void folder2obj(const std::string& strInput, const std::string& stdOutDir, ProgressCallback callback = nullptr);

private:
    ProgressCallback m_callback;
};
```

3. **在处理过程中调用回调函数**

在`osgb2obj.cpp`中,当您处理一个`osgb`时,调用回调函数:

```cpp
m_callback=callback;
for(int i = 0; i < osgb.size(); ++i) {
    // ... 这里是处理osgb的代码 ...

    if(m_callback) { // 检查回调函数是否被设置了
        m_callback(i, osgb.size());
    }
}
```

确保在`folder2obj`函数的实现中,你正确地设置了`m_callback`。

4. **更新`convertMain`以接受回调**

在`convertTools.cpp`中,修改`convertMain`,使其可以接受一个回调函数作为参数,并将其传递给`folder2obj`方法:

```cpp
void convertMain(const std::string& strInput, const std::string& stdOutDir, ProgressCallback callback = nullptr) {
    OSGB2OBJEntity osgb2objentity;
    osgb2objentity.folder2obj(strInput, stdOutDir, callback);
}
```

5. **外部使用**

现在,当外部代码调用`convertMain`时,它可以传入一个回调函数,每次处理`osgb`时都会调用这个函数。

```cpp
convertMain(input, output, [](int current, int total) {
    std::cout << "Processing " << current << " of " << total << std::endl;
});
```

此策略使您的静态库在处理时可以提供实时的反馈,同时不需要对主要的处理逻辑进行大的修改。
我改好了,报错

switch(convertType){
  case 0:
       xxxx调用osgb2obj。
       break;
  case 1:
       xxxx调用osgb2kml。
       break;
   后面等等,其他类型的转换	
}

我这边switch报错。控制传输跳过的实例化:变量 osgb2objentity 
您的错误是由于在`switch`语句中直接定义并初始化了一个局部变量(如`osgb2objentity`),这在C++中是不允许的。

解决这个问题,您可以采用以下策略之一:

1. **使用花括号在`case`语句中创建一个作用域**:
    ```cpp
    switch(convertType) {
      case 0: {
          OSGB2OBJEntity osgb2objentity;
          // xxxx调用osgb2obj。
      }
      break;

      case 1: {
          OSGB2KMLEntity osgb2kmlentity;
          // xxxx调用osgb2kml。
      }
      break;

      // 后面等等,其他类型的转换
    }
    ```

2. **在`switch`语句外部声明变量**:

    如果可能的话,您可以在`switch`语句外部声明并初始化所有可能需要的对象,然后在`case`语句中使用它们。

    ```cpp
    OSGB2OBJEntity osgb2objentity;
    OSGB2KMLEntity osgb2kmlentity;
    // ...其他可能的对象...

    switch(convertType) {
      case 0:
          // xxxx调用osgb2obj。
          break;

      case 1:
          // xxxx调用osgb2kml。
          break;

      // 后面等等,其他类型的转换
    }
    ```

但请注意,这将初始化所有对象,可能会浪费资源,尤其是当这些对象的构造函数有副作用或需要较长时间来执行时。

建议选择第一种方案,因为它只在需要时初始化对象,并为每个`case`提供了一个清晰的作用域。

10、动态库

类似:https://learn.microsoft.com/zh-cn/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-170 

然后在使用的地方,引用头文件、lib库链接、dll放到输出目录下,即可直接使用superConvertTools

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江河地笑

实践是检验真理的唯一标准

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值