地址传递(Call by Reference):形参用原型,是参用原型在地址传递中,实际参数的地址被传递给形式参数,形式参数可以通过该地址来访问和修改实际参数的值。值传递(Call by Value):形参用指针,实参用&在值传递中,函数调用时实际参数的值被复制给形式参数,形式参数在函数内部和外部作用域是独立的,修改形式参数的值不会影响实际参数的值。在C++中,不能直接返回指向局部变量的指针或引用,因为局部变量在函数执行完毕后会被销毁,这样返回的指针或引用指向的内存将不再有效。但是,可以通过值传递来返回局部变量的副本。get()函数引用传递:引用传递是C++特有的一种参数传递方式,使用引用作为形式参数。修改形式参数的值会直接影响实际参数的值。所以在实际应用中,通过使用const int & a作为形参,既避免了值传递复制浪费资源,也避免了在子函数中修改实参
函数指针:int * (*test)(int a,int b);//定义test为函数指针,场景:最基本的回调方法,适用于简单的回调需求。优点:直接、简单,没有额外开销。缺点:不能携带上下文信息,不够灵活。函数对象(functor):场景:适用于需要携带上下文信息或状态的回调。优点:可以携带额外参数,更具灵活性。缺点:编写和管理多个函数对象可能会显得繁琐。#include <iostream>// 定义一个函数对象 AddFunctor,用于计算两个整数的和struct AddFunctor { int operator()(int a, int b) { return a + b; }};int main() { // 创建一个实例化的函数对象 AddFunctor addFunc; // 使用函数对象调用重载的 operator() 函数来计算结果int result = addFunc(3, 4); std::cout << "The result of addFunc(3, 4) is: " << result << std::endl; return 0;}Lambda 表达式:场景:适用于轻量级、匿名的回调需求。优点:简洁、直观,能够轻松捕获外部变量。缺点:可读性可能稍差,不适合复杂逻辑。typedef 语句定义的不是一个函数类型,而是一个函数指针类型。函数指针类型的定义并不需要两个参数,因为它只是指向一个特定类型的函数,而不是定义函数本身。typedef double(*FUNC)(double) 的含义:typedef:用于创建类型别名。double:表示函数返回类型为 double。 (*FUNC):表示 FUNC 是一个指针,指向一个返回类型为 double、参数为 double 的函数。(double):表示该函数接受一个 double 类型的参数。因此,FUNC 是一个指向返回类型为 double、参数为 double 的函数的指针类型的别名。如果想要定义一个函数类型,需要指定函数的参数列表和返回类型,例如:typedef double FUNC(double);typename有两种用法,第一种用于声明模板时,表示模板类型参数,如下所示。在用于模板声明时,typename 和 class 等价,具有同等含义。typename的第二种用法,用于表示一个类内所指的类型为一个 “class” 类型(内置类型、自定类型),而非其他(变量、函数类型)。更一般的说法是,typename 用来告诉编译器类中的嵌套名称表示的是一个类型。看下面一个例子。
强行转换
在 C++ 中,(uint8_t*)(recv_msgs[1].data) 和 static_cast<uint8_t*>(recv_msgs[1].data()) 都可以用于将指针转换为 uint8_t* 类型。两者实际上是相等的,并且在大多数情况下都能正常工作。原始 C 式的强制类型转换 (uint8_t*)(recv_msgs[1].data) 在 C++ 中仍然是有效的,但它通常不被推荐使用。这是因为 C 式的强制类型转换在语法上更灵活,但也更容易出错并丧失了编译器的类型检查能力。C++ 中引入了 static_cast、reinterpret_cast 等更具类型安全性和可读性的强制类型转换操作符。1.使用 static_cast 进行类型转换时,编译器会在转换前检查是否存在合理的类型转换方式,并在编译时进行类型检查,以确保类型转换是安全的。因此,static_cast 在类型转换上比较严格,并且不允许执行具有潜在危险的转换。会改变底层的二进制。通常用于处理阶段之类的2.reinterpret_cast 是 C++ 中的一种类型转换运算符,用于进行底层的重新解释类型转换。不会改变底层的二进制
虚构函数由来父类强调共性,子类强调个性;所以继承之后,需要重写子类的函数,这时候需要一个算法,可以让在需要时,指向需要的子类->这时候的需要找出所有子类的共性->共性即父类的指针/引用->这就解释了为什么要用父类指针存储子类地址,但是又要调用子类,所以必须传子类父类指针指向子类空间带来了一些问题不加虚构函数的时候class Animal{private: /* data */public: void speak() { std::cout << "Animal speak" << std::endl; }};class Dog : virtual public Animal{public: void speak() { std::cout << "Dog speak" << std::endl; }};int main(int argc, char const* argv[]){ //父类指针指向子类空间 Animal* p = new Dog; p->speak();//Animal speak return 0;}仍然会指向父类空间;因为没有声明为虚函数,所以编译器会检查指针类型,根据指针类型类型进行调用;即优先级:虚函数>指针类型想让动态绑定:1.父类定义虚函数 2.子类重写(只有内容不一样)加虚构函数的时候class Animal{private: /* data */public: virtual void speak() = 0;};class Dog : virtual public Animal{public: virtual void speak() //按理来说写不写都可以都会被认为是虚函数,但是必须写上 { std::cout << "Dog speak" << std::endl; }};int main(int argc, char const* argv[]){ //父类指针指向子类空间 Animal* p = new Dog; p->speak(); //Animal speak return 0;}为什么加了virtual就可以了->动态绑定机制c语言实现多态机制:https://blog.csdn.net/weixin_42142630/article/details/123260819动态绑定机制:子类继承父类,会继承父类的虚函数指针和虚函数表,一旦重写父类虚函数,就会在子类中更新虚函数表纯虚函数virtual void name ()=0;->父类不会生成对象(一定)&&子类一定会是重写父类所有的纯虚函数,子类必须全部实现否则也将成为抽象类 ->父类函数体无意义:用纯虚函数:virtual void func()=0;加入=0是为了区别函数的声明->类中有纯虚函数,这个类就是抽象类抽象类必须被继承,如果子类不重写父类纯虚函数,也变成抽象类如果子类开辟了堆区,父类无法释放子类的析构函数,只会释放自己的析构函数虚析构:通过父类的指针,释放子类的空间也是通过vptr,在继承的时候,虚析构指向的vbftable修改为子类的析构函数入口地址,并且自动析构父类基类需要被实例化,并且希望通过基类指针来删除派生类对象时纯虚析构 (通常类内声明,类外实现)本质是析构函数,析构函数为了完成类的清理工作->必须实现函数体基类只用于作为接口规范,不应该被实例化的时候,适合使用纯虚析构函数纯虚析构也需要实现,需要让他跑一遍,清楚父类堆区的内容,类也属于抽象类constexpr:用于计算常量表达式,表示在编译时计算常量的值 使用const ClassDemo & cd作为型参可以减少数据的拷贝,提高性能,const使其不可修改,提高安全性 std::function是C++标准库提供的一个通用的函数封装类模板。 它可以包装任意可调用对象(函数、函数指针、lambda表达式等),并提供统一的调用接口。
lamaba表达式
lamaba表达式auto fut = std::async(std::launch::async, [&]() {0std::async(std::launch::async, [&]():这里调用了 std::async 函数,指定了启动策略为 std::launch::async,表示任务会立即在新线程中执行。Lambda 表达式 [&]()表示创建了一个捕获了当前作用域所有变量的 Lambda 函数。[&] 表示通过引用捕获所有外部变量, 这样可以在 Lambda 内部修改外部变量的值。{}:Lambda 函数的主体,里面包含了实际的任务代码。auto fut =:将 std::async 的返回值(即代表异步执行结果的 std::future 对象)存储在 fut 变量中。通过这个 std::future 对象,可以获取 Lambda 函数执行的结果或等待任务完成。->T&: This specifies the return type of the lambda function. In this case, it returns a reference (&) to an object of type T.This means that when the lambda is called, it returns a reference to an object of type T. The arrow (->) is used to specify the return type in a lambda expression.auto func = [&]() -> int { return 42; };
在C++中,使用 enum class 定义枚举类可以提供更好的封装性和类型安全性,相比传统的 enum 类型,在设计上更为优越。以下是一些 enum class 相对于传统 enum 的优势:作用域限定:enum class 中的枚举值被限定在枚举类的作用域内,不会自动转换为整数类型,避免了与其他作用域中相同名称的变量或函数冲突。类型安全:enum class 是强类型的枚举类,不会自动隐式转换为整数,需要显式地使用枚举类作用域进行访问。可读性:由于 enum class 中的枚举值需要通过作用域来访问,可以提高代码的可读性,避免不小心混淆或误用不同枚举类型的情况。编译时检查:使用 enum class 可以在编译时捕获一些潜在的错误,比如将不同类型的枚举值相互比较或赋值。总而言之,通过使用 enum class 并将枚举值放在类的作用域中,可以提高代码的安全性、可读性和健壮性。因此,推荐在 C++ 中使用 enum class 来定义枚举类型。
#include <iostream>int main() { int x = 5; { int y = 10; std::cout << "Inside code block: x = " << x << ", y = " << y << std::endl; } // 在这里无法访问 y,因为 y 的作用域仅限于上面的代码块内// std::cout << "Outside code block: x = " << x << ", y = " << y << std::endl; // 这行代码会导致编译错误return 0;}