C和CPP语言语法集

1. c++调用的函数前加 "::"代表全局作用域的

2.__attribute__ ((format (printf, 2, 3)));

功能:
   __attribute__ format属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。format属性告诉编译器,按照printf, scanf等标准C函数参数格式规则对该函数的参数进行检查。这在我们自己封装调试信息的接口时非常的有用。

format的语法格式为:
format (archetype, string-index, first-to-check)
  其中,“archetype”指定是哪种风格;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数开始按上述规则进行检查。
具体的使用如下所示:
__attribute__((format(printf, m, n)))
__attribute__((format(scanf, m, n)))

  其中参数m与n的含义为:
    m:第几个参数为格式化字符串(format string);
    n:参数集合中的第一个,即参数“…”里的第一个参数在函数参数总数排在第几。注意,有时函数参数(类成员函数)里还有“隐身”的(this指针);

如:extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));   //m=1;n=2      
原文链接:https://blog.csdn.net/zzhongcy/article/details/90057289

3.std::move简要说明

std::move是 C++11 引入的一个工具,它可以将一个对象标记为“可移动”,从而允许我们将其资源转移给另一个对象,而不是复制。这是通过转换其参数为右值引用来实现的,从而触发移动构造函数或移动赋值操作符,而不是复制构造函数或复制赋值操作符。

/**
 *  @brief  Convert a value to an rvalue.
 *  @param  __t  A thing of arbitrary type.
 *  @return The parameter cast to an rvalue-reference to allow moving it.
*/
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type&&>(t);
}

如:

#include <utility> // For std::move

// 假设 MyClass 有移动构造函数和移动赋值操作符定义
MyClass a;
MyClass b = std::move(a); // 调用 MyClass 的移动构造函数

注意

  • 使用 std::move 之后,源对象 a 和 b 仍然存在,但是它们的内容已经被转移走了。因此,除了销毁或赋予新值之外,不应该再使用这些对象。
  • 即使调用 std::move,也不一定会发生资源移动。如果类没有提供移动构造函数或移动赋值操作符,将会回退到调用复制构造函数或复制赋值操作符。
  • 在移动操作之后,源对象通常被置于所谓的“空”状态,但具体状态依赖于移动操作的实现。因此,在移动操作后,最好不要对源对象进行操作,除非类的文档明确说明了移动后的状态。
  • std::move 本身并不移动任何东西;它只是将其参数转换为右值引用,以便可以调用移动构造函数或移动赋值操作符。
#include <iostream>
#include <algorithm> // For std::copy
#include <utility>   // For std::move

class MyClass {
public:
    // 构造函数
    MyClass(int size) : size_(size), data_(new int[size]) {
        std::cout << "调用构造函数" << std::endl;
    }

    // 复制构造函数
    MyClass(const MyClass& other) : size_(other.size_), data_(new int[other.size_]) {
        std::copy(other.data_, other.data_ + size_, data_);
        std::cout << "调用复制构造函数" << std::endl;
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : size_(other.size_), data_(other.data_) {
        other.size_ = 0;
        other.data_ = nullptr;
        std::cout << "调用移动构造函数" << std::endl;
    }

    // 复制赋值操作符
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            delete[] data_;
            size_ = other.size_;
            data_ = new int[size_];
            std::copy(other.data_, other.data_ + size_, data_);
            std::cout << "调用复制赋值操作符" << std::endl;
        }
        return *this;
    }

    // 移动赋值操作符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            size_ = other.size_;
            data_ = other.data_;
            other.size_ = 0;
            other.data_ = nullptr;
            std::cout << "调用移动赋值操作符" << std::endl;
        }
        return *this;
    }

    // 析构函数
    ~MyClass() {
        delete[] data_;
        std::cout << "调用析构函数" << std::endl;
    }

private:
    int* data_;
    int size_;
};

int main() {
    MyClass a(10); // 构造函数被调用

    MyClass b(a); // 复制构造函数被调用

    MyClass c(std::move(a)); // 移动构造函数被调用

    b = c; // 复制赋值操作符被调用

    c = std::move(b); // 移动赋值操作符被调用

    return 0;
}

总的来说,std::move 是一个非常有用的工具,它可以帮助我们优化性能,特别是涉及到大型对象或资源密集型对象(如动态数组、文件句柄、套接字等)的场景。正确使用 std::move 可以避免不必要的复制,从而提高应用程序的效率。

不过,同时应该谨慎使用它。在使用std::move之后,原始对象会处于一个有效但未定义的状态,因此除非我们确定之后不再需要该对象,或者只会对其进行销毁或赋予新值,否则不应该使用std::move。此外,过度使用std::move有时会阻止编译器进行某些优化,如复制省略 (copy elision)。

C++的std::move与std::forward原理

其实std::move并不能移动任何东西,它唯一的功能是将一个左值/右值强制转化为右值引用,继而可以通过右值引用使用该值,所以称为移动语义

std::move的作用:将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能。 它是怎么个转移法,将在文章的最后面解释。

看到std::move的代码,意味着给std::move的参数,在调用之后,就不再使用了。

template<typename T>
void func( T&& param){
    
}
func(5);  //15是右值, param是右值引用
int a = 10; //
func(a); //x是左值, param是左值引用

这里的&&是一个未定义的引用类型,称为
通用引用 Universal References
(https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers)

它必须被初始化,它是左值引用还是右值引用却决于它的初始化,如果它被一个左值初始化,它就是一个左值引用;如果被一个右值初始化,它就是一个右值引用。

注意,只有当发生自动类型推断时(如函数模板的类型自动推导,或auto关键字),&&才是一个Universal References。

引用折叠

#include <iostream>
#include <type_traits>
#include <string>
using namespace std;

template<typename T>
void func(T&& param) {
    if (std::is_same<string, T>::value)
        std::cout << "string" << std::endl;
    else if (std::is_same<string&, T>::value)
        std::cout << "string&" << std::endl;
    else if (std::is_same<string&&, T>::value)
        std::cout << "string&&" << std::endl;
    else if (std::is_same<int, T>::value)
        std::cout << "int" << std::endl;
    else if (std::is_same<int&, T>::value)
        std::cout << "int&" << std::endl;
    else if (std::is_same<int&&, T>::value)
        std::cout << "int&&" << std::endl;
    else
        std::cout << "unkown" << std::endl;
}

int getInt() {
    return 10;
}

int main() {
    int x = 1;
    func(1); // 传递参数是右值 T推导成了int, 所以是int&& param, 右值引用
    func(x); // 传递参数是左值 T推导成了int&, 所以是int&&& param, 折叠成 int&,左值引用
    func(getInt());// 参数getInt是右值 T推导成了int, 所以是int&& param, 右值引用

    return 0;
}

std::foward

  /**
   *  @brief  Forward an lvalue.
   *  @return The parameter cast to the specified type.
   *
   *  This function is used to implement "perfect forwarding".
   */
  template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type& __t) noexcept
    { return static_cast<_Tp&&>(__t); }

  /**
   *  @brief  Forward an rvalue.
   *  @return The parameter cast to the specified type.
   *
   *  This function is used to implement "perfect forwarding".
   */
  template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
    {
      static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
            " substituting _Tp is an lvalue reference type");
      return static_cast<_Tp&&>(__t);
    }

有两个函数:
第一个,参数是左值引用,可以接受左值。
第二个,参数是右值引用,可以接受右值。

根据引用折叠的原理,如果传递的是左值,Tp推断为string&,则返回变成
static_cast<string& &&>,也就是static_cast<string&>,所以返回的是左值引用。

如果传递的是右值,Tp推断为string或string&&,则返回变成
static_cast<string&&>,所以返回的是右值引用。

反正不管怎么着,都是一个引用,那就都是别名,也就是谁读取std::forward,都直接可以得到std::foward所赋值的参数。

这就是完美转发的基本原理!

c++ 之 std::move 原理实现与用法总结

[c++11]我理解的右值引用、移动语义和完美转发
https://blog.csdn.net/newchenxf/article/details/117995131

4.仿函数

仿函数(functor),就是使一个的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

扩展:标识符函数identity(c++20开始)

template<typename T>
struct IdentityFunctor_T
{
    inline const T& operator()(const T& arg) const {return arg;}
}

Defined in header <functional>

接收一个参数并返回该不可变参数。多用于排序等场景中。

posted @ 2024-02-23 17:20  hugingface  阅读(121)  评论(0)    收藏  举报