c++ 1个数组&指针转换以及容器使用实例

// 如果指针没有保存数据元素个数,可通过设置变量(num)保存指针起始地址到结束地址之间元素个数,
// 进行指针偏移来进行元素个数遍历时,可使用数组的形式通过数组下标(0~num)进行遍历
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef void (* cbPrint)(int id, const std::string & str);
typedef struct {
	int 	tskId;
	bool 	run;
	cbPrint print;
	std::string str;
} TaskMsg;

void Print(int id,  const std::string & str)
{
	std::cout << "info[" << id << "] " << str << std::endl;
}

TaskMsg tsk1[] = {
	{0, true, Print,	"hello world 0"},\
	{1, true, Print,	"hello world 1"},\
	{2, true, Print,	"hello world 2"}\
};

TaskMsg tsk2[] = {
	{0, true, Print,	"hello c++ 0"},\
	{1, true, Print,	"hello c++ 1"},\
};

class Rpt {
public:
    #define TYPE 0
    // 对指针`TaskMsg*`类型赋值的时候只能取数组的第一个元素的地址。
    // 这个指针并没有记录数组的长度,因此需要添加一个成员变量记录数组的长度,以便在遍历时知道循环的次数。
    typedef struct {
        int     num;
        TaskMsg *task;
    } TaskInfo;

public:
	Rpt() {
        m_task = new TaskInfo;
		m_task->num = (!TYPE) ? (sizeof(tsk1) / sizeof(tsk1[0])) : (sizeof(tsk2) / sizeof(tsk2[0]));
        // 对指针`TaskMsg*`类型赋值的时候只能取数组的第一个元素的地址。
		m_task->task = (!TYPE) ? tsk1 : tsk2;
	}
	~Rpt() {
        delete m_task;
    }
	void run() {
		for (int i = 0; i < m_task->num; i++)
		{
			if (m_task->task[i].run && m_task->task[i].print)
			{
				m_task->task[i].print(m_task->task[i].tskId, m_task->task[i].str);
			}
		}
	}
private:
	TaskInfo *m_task;
};

int main()
{
	Rpt rptObj;
	rptObj.run();
	return 0;
}

// 如果指针没有保存数据元素个数,可通过设置变量(num)保存指针起始地址到结束地址之间元素个数,
// 进行指针偏移来进行元素个数遍历时,可使用指针偏移(offset[0~num-1])的形式进行元素遍历
// 在 C++ 中,指针的加法运算实际上是指针类型和偏移量的乘积,即:p + n = p + n * sizeof(*p)
// 也就是说,每次对指针进行加 1 操作时,指针实际上会向后偏移 `sizeof(*p)` 个字节
// 所以,每次遍历完当前的任务时,要将 `m_task->task` 指针向右移动一个 `TaskInfo` 的长度,
// 即 `m_task->task++`。这样才能正确地指向下一个任务的信息。

// 在 C++ 中,指针的长度和指针变量的长度是不一样的。
// 指针的长度和系统的位数有关系,32位的系统通常采用4字节的指针,64位的系统通常采用8字节的指针。
// 指针的长度仅与系统的位数有关系,而指针变量的长度是根据指针指向的数据类型来计算的,即为指针指向类型的大小。
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef void (* cbPrint)(int id, const std::string & str);
typedef struct {
	int 	tskId;
	bool 	run;
	cbPrint print;
	std::string str;
} TaskMsg;

void Print(int id,  const std::string & str)
{
	std::cout << "info[" << id << "] " << str << std::endl;
}

TaskMsg tsk1[] = {
	{0, true, Print,	"hello world 0"},\
	{1, true, Print,	"hello world 1"},\
	{2, true, Print,	"hello world 2"}\
};

TaskMsg tsk2[] = {
	{0, true, Print,	"hello c++ 0"},\
	{1, true, Print,	"hello c++ 1"},\
};

class Rpt {
public:
    #define TYPE 0
    // 对指针`TaskMsg*`类型赋值的时候只能取数组的第一个元素的地址。
    // 这个指针并没有记录数组的长度,因此需要添加一个成员变量记录数组的长度,以便在遍历时知道循环的次数。
    typedef struct {
        int     num;
        TaskMsg *task;
    } TaskInfo;

public:
	Rpt() {
        m_task = new TaskInfo;
		m_task->num = (!TYPE) ? (sizeof(tsk1) / sizeof(tsk1[0])) : (sizeof(tsk2) / sizeof(tsk2[0]));
        // 对指针`TaskMsg*`类型赋值的时候只能取数组的第一个元素的地址。
		m_task->task = (!TYPE) ? tsk1 : tsk2;
	}
	~Rpt() {
        delete m_task;
    }
	void run() {
		for (int i = 0; i < m_task->num; i++)
		{
			if (m_task->task->run && m_task->task->print)
			{
				m_task->task->print(m_task->task->tskId, m_task->task->str);
			}
            m_task->task++;
		}
	}
private:
	TaskInfo *m_task;
};

int main()
{
	Rpt rptObj;
	rptObj.run();
	return 0;
}

// 在 C++ 中,定义的指针变量并不知道指向数组的长度,因此必须要借助 `num` 这样的变量存储数组长度。
// 如果希望不依赖 `num` 来遍历 `m_task` 中的元素,可以在 `TaskMsg` 数组的最后一个元素后面插入一个 `NULL` 指针,表示数组的结束。
// 在遍历的时候,判断指针值是否为 `NULL`,遇到 `NULL` 就退出遍历即可。
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef void (* cbPrint)(int id, const std::string & str);
typedef struct {
	int 	tskId;
	bool 	run;
	cbPrint print;
	std::string str;
} TaskMsg;

void Print(int id,  const std::string & str)
{
	std::cout << "info[" << id << "] " << str << std::endl;
}

TaskMsg tsk1[] = {
	{0, true,  Print,	"hello world 0"},\
	{1, true,  Print,	"hello world 1"},\
	{2, true,  Print,	"hello world 2"},\
    {0, false, nullptr,	""}\
};

TaskMsg tsk2[] = {
	{0, true, Print,	"hello c++ 0"},\
	{1, true, Print,	"hello c++ 1"},\
    {0, false, nullptr,	""}\
};

class Rpt {
public:
    #define TYPE 1
	Rpt() {
        // m_task = new TaskMsg;
        // 构造函数中,不需要使用 `new` 关键字动态分配内存给 `m_task`,因为 `m_task` 所指向的内存是静态分配的数组,不需要释放内存。
        // 正确的实现方式是在构造函数中直接让 `m_task` 指向静态分配的数组,而在析构函数中不进行任何操作。
        // 对指针`TaskMsg*`类型赋值的时候只能取数组的第一个元素的地址。进行遍历时需要进行指针偏移
		m_task = (!TYPE) ? tsk1 : tsk2;
	}
	~Rpt() {
        // free(m_task);
        // 如果在这儿释放内存会出现如下报错
        // free(): invalid pointer
        // Aborted (core dumped)
        // 原因是因为在析构函数中使用了 `free` 函数来释放 `m_task` 所指向的内存,
        // 但是 `m_task` 所指向的内存是静态分配的数组,不是使用 `malloc` 等动态内存分配函数分配的内存,因此不能使用 `free` 函数对其进行释放。
        // 注意,只有使用 `malloc`、`calloc`、`realloc` 等动态内存分配函数分配的内存才需要使用 `free` 函数进行释放,
        // 使用 `new` 关键字动态分配的内存需要使用 `delete` 关键字进行释放。
        // 对于静态分配的内存,我们不需要进行显式的释放操作。
    }
	void run() {
		for (TaskMsg *curTsk = m_task; curTsk->run && curTsk->print; ++curTsk)
		{
			curTsk->print(curTsk->tskId, curTsk->str);
		}
	}
private:
	TaskMsg *m_task;
};

int main()
{
	Rpt rptObj;
	rptObj.run();
	return 0;
}

// 使用`vector`的版本比使用指针和数组的版本更加安全和易用。
// `vector` 类自动管理内存,避免了指针和数组使用中一些常见的内存管理问题(如内存泄漏、越界访问等)。
// `vector` 还提供了一些常用的方法和算法,简化了对元素的访问和处理。
// 此外,`vector` 支持动态大小调整,可以有效地避免了数组定义时大小不合适的问题。

// 使用了 `vector` 后,可以不需要数组的最后一个结束标志的元素,也不需要使用 num 来标记元素个数。
// 因为 `vector` 自带尺寸信息,并且提供了迭代器等方法可以方便地遍历元素。在遍历时,只需要使用 `for` 循环进行迭代即可。
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <string.h>
#include <vector>

typedef void (* cbPrint)(int id, const std::string & str);
typedef struct {
	int 	tskId;
	bool 	run;
	cbPrint print;
	std::string str;
} TaskMsg;

void Print(int id,  const std::string & str)
{
	std::cout << "info[" << id << "] " << str << std::endl;
}

std::vector<TaskMsg> tsk1 = {
	{0, true,  Print,	"hello world 0"},\
	{1, true,  Print,	"hello world 1"},\
	{2, true,  Print,	"hello world 2"},\
    {0, false, nullptr,	""}\
};

std::vector<TaskMsg> tsk2 = {
	{0, true, Print,	"hello c++ 0"},\
	{1, true, Print,	"hello c++ 1"},\
    {0, false, nullptr,	""}\
};

class Rpt {
public:
    #define TYPE 0
	Rpt() {
        // 使用容器`vector`来存储任务列表
		m_task = (!TYPE) ? tsk1 : tsk2;
	}
	~Rpt() {}
	void run() {
		for (auto & task : m_task)
        {
            if (task.run && task.print)
            {
                task.print(task.tskId, task.str);
            }
        }
	}
private:
	std::vector<TaskMsg> m_task;
};

int main()
{
	Rpt rptObj;
	rptObj.run();
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值