C++语法|指向类成员(成员变量和成员方法)的指针及其相关应用场景

1.基本语法

指向类成员的指针是一种特殊的指针类型,用于指向类的成员变量或成员函数。与普通指针不同,指向类成员的指针不能单独使用,必须结合特定的类对象来进行访问

指向成员变量的指针

指向成员变量的指针用于指向某个类的成员变量。其语法如下(重点是要声明作用域)

class A {
public:
    MemberType member;
};

// 定义指向成员变量的指针
MemberType A::*ptr = &A::member;

示例

#include <iostream>
using namespace std;
class MyClass {
public:
	int data;
}

int main() {
	MyClass obj;
	obj.data = 10;
	//定义一个指向成员变量的指针
	int MyClass::*ptr = &MyClass::data;
	cout << "data: " << pbj.data << endl;
	cout << "data using pointer: " << obj.*ptr << endl;
}

指向成员函数的指针

class A {
public:
    ReturnType Function(Arg);
};

// 定义指向成员函数的指针
ReturnType (A::*pointerToFunction)(Arg) = &A::Function;

示例

#include <iostream>
using namespace std;
class MyClass {
public:
    void display() {
        cout << "Hello, World!" << endl;
    }
};
int main () {
	MyClass obj;
	void (MyClass::*funcPtr)() = &MyClass::display;
	
	//通过对象调用成员函数
	(obj.*funcPtr)();
	//通过指针调用成员函数
	MyClass* objPtr = &obj;
	(objPtr->*funcPtr)();
	return 0;
}

注意事项

我们定义一个包含成员方法、静态成员方法、成员变量的类:
一:如何通过指向类成员变量的指针来访问类成员

class Test {
public:
	void func() { cout << "call Test::func" << endl; }
	static void static_func() { cout << "Test::static_func" << endl; }
	static int mb;
	int ma;
};
int main () {
	Test t1;

	//int *p = &Test::ma //报错 无法从“int Test::* ” 转换成 "int *"
	int Test::*p = &Test::ma;
	//*p = 20; 报错,因为我们都没有初始化一个 Test对象,类不能脱离对象谈成员
	//应该先进行初始化,也就是在前面加上 Test t1;
	t1.*p = 20;
}

总结:普通的成员变量必须依赖一个对象,所以即使操作外部指针也应该依赖一个对象,当然如果我们操作一个静态成员变量,它是不依赖对象的,跟普通指针使用一样

int Test::mb;
int main () {
	int *p1 = &Test::mb;
	*p1 = 40; //mb为40
}

二、指向成员方法的指针
我们有一个普通的成员方法void func()我们如何定义一个函数指针来指向这个函数呢?

int main () {
	//如果是普通函数中是可以的,但是C++中不被允许
	//无法从"void (thiscall Test::*)(void)"转换为"void (__cdecl *)(void)"
	//void(*pfunc)() = &Test::func;
	//(*pfunc)();
	
	Test t1;
	Test *t2 = new Test();
	void (Test::*pfunc)() = &Test::func;
	(t1.*pfunc)();	//通过解引用来调用
	(t2->*pfunc)();
}

三、指向静态成员方法的指针
跟之前的静态成员变量一样,可以当做一个普通指针来使用,不需要初始化一个类。
静态成员方法的调用不需要依赖一个对象,他就是一个普通的C函数,只不过现在落在了类的作用域里面而已

2.应用场景

泛型编程和模板:通用成员访问打印函数

在模板编程中,指向成员的指针可以用于编写通用代码,能够操作不同类型对象的成员。这在编写需要操作不同类型对象的函数模板时非常有用。

假设我们有几个不同的类,每个类都有各自不同的成员变量。我们希望编写一个通用的函数,该函数可以接受任何对象和该对象类的任何成员变量的指针,并打印出该成员的值。这种方法在处理多种数据类型的统一处理程序(如日志、序列化、UI显示等)时非常有用。

  1. 首先我们定义两个示例类:
// 定义两个示例类
class Person {
public:
    string name;
    int  age;

    Person(const string& name, int age) : name(name), age(age) {}
};

class Product {
public:
    string title;
    double price;

    Product(const string& title, double price) : title(title), price(price) {}
};
  1. 我们想定义一个通用的打印函数,可以打印不同对象的不同成员
int main() {
    Person alice("Alice", 30);
    Product laptop("Laptop", 999.99);

    // 打印不同对象的不同成员
    display(alice, &Person::name);  // 打印 Person 对象的 name 成员
    display(alice, &Person::age);   // 打印 Person 对象的 age 成员
    display(laptop, &Product::title);  // 打印 Product 对象的 title 成员
    display(laptop, &Product::price);  // 打印 Product 对象的 price 成员

    return 0;
}

你能帮我编写出来吗?


答案:

//通用的打印函数模板
template<typename T, typename M>
void display(const T&obj, M T::*member) {
    cout << obj.*member << endl;
}
  • 灵活性:printMember 函数可以用于任何类和任何类型的成员变量。
  • 重用性:不需要为每种类型或每个成员单独编写打印逻辑。
  • 简洁性:代码更加简洁和容易维护,因为成员访问逻辑只编写一次。

回调机制和事件处理:基于简单GUI框架的事件处理

假设我们正在开发一个GUI应用程序,其中包含多个按钮,每个按钮点击时都需要执行不同的操作。我们可以定义一个Button类,该类包含一个成员函数指针,当按钮被点击时,该指针指向的成员函数将被调用。

Step 1: 定义Button类

首先,我们定义一个Button类,它包含一个指向成员函数的指针作为回调。
我们需要定义 Button 类以存储指向成员函数的指针和一个指向调用它的对象的指针。这里的一个关键点是确保成员函数指针的类型正确,并且我们还需要知道哪个类的成员函数将被调用。下面是如何实现这个:

template <typename T>
class Button {
public:
    using EventCallback = void (T::*)();  // 成员函数指针类型

    Button() : callbackObject(nullptr), onClick(nullptr) {}

    // 设置回调函数及其关联的对象
    void setOnClick(EventCallback callback, T* obj) {
        onClick = callback;
        callbackObject = obj;
    }

    // 模拟按钮点击
    void click() {
        if (callbackObject && onClick) {
            std::cout << "Button clicked.\n";
            (callbackObject->*onClick)();  // 使用成员函数指针调用函数
        }
    }

private:
    T* callbackObject;  // 指向调用成员函数的对象的指针
    EventCallback onClick;  // 成员函数指针
};

Step 2: 定义具体操作
我们定义一个Application类,该类包含多个成员函数,每个函数都可以作为按钮的回调。

class Application {
public:
    void displayMessage() {
        std::cout << "Displaying message!\n";
    }

    void openFile() {
        std::cout << "Opening file...\n";
    }

    void shutdown() {
        std::cout << "Shutting down...\n";
    }
};

Step 3: 绑定事件与成员函数
我们需要将按钮的点击事件绑定到特定的成员函数。这里我们使用std::function和std::bind来完成绑定,因为直接使用指向成员函数的指针需要处理对象的具体实例,而std::function和std::bind提供了更灵活的方式来处理这种情况。

int main() {
    Application app;
    Button<Application> btnMessage, btnFile, btnShutdown;

    // 设置按钮的回调
    btnMessage.setOnClick(&Application::displayMessage, &app);
    btnFile.setOnClick(&Application::openFile, &app);
    btnShutdown.setOnClick(&Application::shutdown, &app);

    // 模拟按钮点击
    btnMessage.click();
    btnFile.click();
    btnShutdown.click();

    return 0;
}

总结

这种方式直接使用模板和指向成员函数的指针。此外,它为每个按钮提供了泛型支持,这意味着 Button 类可以与任何类的任何成员函数一起使用,只要这些函数满足返回值 void ,传参()。

使用模板的另一个好处是你可以在编译时捕获类型错误,如尝试绑定到非法函数或不正确的对象。然而,这种方法的限制是它只能与无参数且返回 void 的成员函数一起工作,如果需要更复杂的回调签名,你可能需要修改模板定义来支持这些。

其实,本节比较难的应该是属于函数指针了,但是在C++中常常使用function和bind来替代函数指针的使用场景。。。
扩展阅读:
C语言学习细节|C语言面向对象编程!函数指针如何正确使用
C++语法|可调用对象与function类型
C++语法|bind和function解析并实现一个简易线程池
C++语法|bind1st和bind2nd的用法

### RT-DETRv3 网络结构分析 RT-DETRv3 是一种基于 Transformer 的实时端到端目标检测算法,其核心在于通过引入分层密集正监督方法以及一系列创新性的训练策略,解决了传统 DETR 模型收敛慢解码器训练不足的问题。以下是 RT-DETRv3 的主要网络结构特点: #### 1. **基于 CNN 的辅助分支** 为了增强编码器的特征表示能力,RT-DETRv3 引入了一个基于卷积神经网络 (CNN) 的辅助分支[^3]。这一分支提供了密集的监督信号,能够与原始解码器协同工作,从而提升整体性能。 ```python class AuxiliaryBranch(nn.Module): def __init__(self, in_channels, out_channels): super(AuxiliaryBranch, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(self.conv(x))) ``` 此部分的设计灵感来源于传统的 CNN 架构,例如 YOLO 系列中的 CSPNet PAN 结构[^2],这些技术被用来优化特征提取效率并减少计算开销。 --- #### 2. **自注意力扰动学习策略** 为解决解码器训练不足的问题,RT-DETRv3 提出了一种名为 *self-att 扰动* 的新学习策略。这种策略通过对多个查询组中阳性样本的标签分配进行多样化处理,有效增加了阳例的数量,进而提高了模型的学习能力泛化性能。 具体实现方式是在训练过程中动态调整注意力权重分布,确保更多的高质量查询可以与真实标注 (Ground Truth) 进行匹配。 --- #### 3. **共享权重解编码器分支** 除了上述改进外,RT-DETRv3 还引入了一个共享权重的解编码器分支,专门用于提供密集的正向监督信号。这一设计不仅简化了模型架构,还显著降低了参数量推理时间,使其更适合实时应用需求。 ```python class SharedDecoderEncoder(nn.Module): def __init__(self, d_model, nhead, num_layers): super(SharedDecoderEncoder, self).__init__() decoder_layer = nn.TransformerDecoderLayer(d_model=d_model, nhead=nhead) self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers) def forward(self, tgt, memory): return self.decoder(tgt=tgt, memory=memory) ``` 通过这种方式,RT-DETRv3 实现了高效的目标检测流程,在保持高精度的同时大幅缩短了推理延迟。 --- #### 4. **与其他模型的关系** 值得一提的是,RT-DETRv3 并未完全抛弃经典的 CNN 技术,而是将其与 Transformer 结合起来形成混合架构[^4]。例如,它采用了 YOLO 系列中的 RepNCSP 模块替代冗余的多尺度自注意力层,从而减少了不必要的计算负担。 此外,RT-DETRv3 还借鉴了 DETR 的一对一匹配策略,并在此基础上进行了优化,进一步提升了小目标检测的能力。 --- ### 总结 综上所述,RT-DETRv3 的网络结构主要包括以下几个关键组件:基于 CNN 的辅助分支、自注意力扰动学习策略、共享权重解编码器分支以及混合编码器设计。这些技术创新共同推动了实时目标检测领域的发展,使其在复杂场景下的表现更加出色。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值