Showing posts with label c plusplus. Show all posts
Showing posts with label c plusplus. Show all posts

Sunday, March 20, 2022

[C++] Primer - 函数参数传递中值传递、地址传递、引用传递有什么区别?


實參 (argument)

全称为"实际参数"是在调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。

形参(parameter)

全称为"形式参数" 由于它不是实际存在变量,所以又称虚拟变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。因而,必须注意实参的个数,类型应与形参一一对应,并且实参必须要有确定的值。

形式参数:形参是函数被调用时用于接收实参值的变量


(1) 值传递,会为形参重新分配内存空间,将实参的值拷贝给形参,形参的值不会影响实参的值,函数调用结束后形参被释放;


(2) 引用传递,不会为形参重新分配内存空间,形参只是实参的别名,形参的改变会影响实参的值,函数调用结束后形参不会被释放;


(3) 地址传递,形参为指针变量,将实参的地址传递给函数,可以在函数中改变实参的值,调用时为形参指针变量分配内存,结束时释放指针变量


[C++] Primer - 大多数编译错误在实例化期间报告

模板直到实例化时才生成代码,所以获得模板代码编译错误的时机较晚。编译器在3个阶段报告错误:

1. 第一阶段是编译模板本身时,此时一般错误很少,只是检查语法错误,不检查依赖于类型的代码

第二个阶段是遇到使用模板时,对函数模板调用编译器会检查实参数目是否正确。还要检查参数类型是否匹配。对类模板来说,编译器检查用户是否提供了正确的模板实参,但也仅限于此。

第三个阶段是模板实例化时,只有这个阶段才能发现类型相关的错误。依赖于编译器如何管理实例化,这类错误可能在链接时才报告。

编写模板代码不能针对特定类型,但模板代码通常对其所用的类型有一些假设。比如compare就会假设实参支持<运算符。

如果实例化T类型不支持<,那么就会在第三个阶段报错。

[C++] Primer - 1.1.6 模板編譯

 编译器遇到一个模板定义时,并不生成代码。只有当实例化模板时编译器才生成代码。

当调用一个函数时,编译器只需要掌握函数的声明,函数定义不必已经出现,即使不定义,编译也会通过,最终会在链接时才发现undefined symbol这个熟悉的错误

但对于模板来说却不同,生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义。因此,与非模板代码不同,模板的头文件通常既包含声明也包含定义。

函数模板和类模板成员函数定义通常在头文件中。


[C++] Primer - 1.1.5 编写类型无关的代码

泛型代码要适配各种用到的类型。compare虽然简单,但说明了编写泛型代码的两个重要原则:

1. 函数参数是const引用

2. 条件判断仅仅使用<运算符

通过将函数参数设定为const引用,就保证了函数可以用于不能拷贝的类型。不拷贝也提高了效率。

仅仅使用<运算符降低了compare对处理类型的要求,只要适配的类型支持<运算符,就可以应用compare。


归根结底,核心的思想就是让模板程序尽量降低对实参类型的要求

 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据

[C++] Primer - inline 和 constexpr 函数模板

 函数模板可以声明为inline或constexpr的,如同非模板函数一样

template <typename T>

inline T min(const T&, const T&);


[C++] Primer - 非类型模板参数

除了定义类型参数,还可以定义非类型参数(nontype parameter)。非类型参数表示一个值而非一个类型。

模板被实例化时,非类型参数被一个用户提供的或编译器推断出的值所代替。这些值必须是常量表达式,从而允许编译器在编译时实例化模板。

template<unsigned N, unsigned M>

int compare(const char (&p1)[N], const char (&p2)[M])

{

    return strcmp(p1, p2);

}

处理两个字符数组,两个非类型模板参数是unsigned int值,该值在调用时确定,可以由程序员显式指定,也可以隐式推断

compare("hi", "mom");

//编译器会用字面常量的大小来代替N和M,从而实例化模板。

// int compare(const char (&p1)[3], const char (&p2)[4])

非类型参数可以是一个整型,或是一个指向对象或函数类型的指针或左值引用。

绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。不能用一个普通局部变量或动态对象作为指针或引用非类型模板参数的实参。指针参数可以用nullptr或一个值为0的常量表达式来实例化。


模板定义内,模板非类型参数是一个常量值。在需要常量表达式的地方,可以使用非类型参数,例如,指定数组大小。

[C++] Primer - 模板類型參數

 返回类型和参数类型相同

template <typename T>

T foo(T* p)

{

    T tmp = *p; // tmp的类型将是指针p指向的类型

    //...

    return tmp;

}

如果类型参数不止一个,那么他们不能同名,且每一个前面都需要typename关键字,用逗号隔开。

template <typename T, typename U>

T calc(const T&, const U&);

Saturday, March 19, 2022

[C++] Primer - 模板与泛型编程

為何要用泛型

1.1 定義模板

  1.1.1 函數模板

    1.1.1.1 實例化模板

    1.1.1.2 模板類型參數

    1.1.1.3 非類型模板參數 (compare)

    1.1.1.4 inline和constexpr函数模板

    1.1.1.5 编写类型无关的代码

    1.1.1.6 模板編譯

    1.1.1.7 大多数编译错误在实例化期间报告

  1.1.2 類模板

  1.1.3 模板參數

  1.1.4成員模板

  1.1.5 控制實例化

1.2 模板實參推斷


2 模板实参推断

3  重载与模板

4 可變參數模板

5 模板特例化



Reference:

https://www.cnblogs.com/fortunely/p/14564383.html


[C++] Primer - 实例化模板

 实例化模板

由于模板compare只是一个pattern,所以在使用参数调用它时要先确定T的类型,这一过程叫做模板实例化

//实例化出int compare(const int &, const int &)

cout << compare(1,0) << endl; //T为int

//实例化出int compare(const vector<int>&, const vector<int>&)

vector<int> vec1{1, 2, 3}, vec2{4, 5, 6};

cout << compare(vec1, vec2) << endl; //T为vector<int>

Monday, March 14, 2022

[C++] Primer - 函數模板

当调用一个函数时,编译器通常用函数实参来推断模板实参,用此函数实参类型代替模板实参创建出一个新的“实例”,即一个可调用的函数,这个过程叫实例化(instantiate)函数模板。该函数是得到的一个特定版本的函数。

编译器生成的函数版本,通常称为模板的实例。

 int compare(const string &v1, const string &v2)

{

    if(v1 < v2) return -1;

    if(v2 < v1) return 1;

    return 0;

}

int compare(const double &v1, const double &v2)

{

    if(v1 < v2) return -1;

    if(v2 < v1) return 1;

    return 0;

}

// 定义compare的函数模板 // compare声明了类型为T的类型参数 // template关键字,<typename T>是模板参数列表,typename和class关键字等价,都可以使用,T是模板参数,以逗号分隔其他模板参数

template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; else if (v1 > v2) return 1; return 0; }

// 调用模板,将模板实参绑定到模板参数(T)上 // 调用函数模板时,编译器根据函数实参(1,0)来推断模板实参

cout << compare(1, 0) << endl; // 推断T为int

// 编译器会根据调用情况,推断出T为int,从而生成一个compare版本,T被替换为int

int compare(const T &v1, const T &v2)

{

    if(v1 < v2) return -1;

    if(v2 < v1) return 1;

    return 0;

}


Sunday, February 13, 2022

[Linux Kernel] pipeline

沒有管線化(pipeline)執行跟有管線化執行的差別

1. 沒有管線化執行不會發生危障(hazard

2. 沒有管線化執行的效能較差.

3. 管線化執行能增加指令同時執行的數量

4. 單一指令的執行時間都是固定的

Linux 效能分析工具: Perf

[c++] Overloading class member access (C++ only)

 #include <iostream>

using namespace std;


struct Y {

  void f() {

    cout << "call f()" << endl;

  };

};


struct X {

 Y* ptr;

 Y* operator->() {

   return ptr;

 };

};


int main() {

  X x;

  x->f();

}

call f()

[c++] Overloading subscripting (C++ only)

 #include <iostream>

using namespace std;


template <class T> class MyArray {

private:

  T* storage;

  int size;

public:

  MyArray(int arg = 10) {

    storage = new T[arg];

    size = arg;

  }


  ~MyArray() {

    delete[] storage;

    storage = 0;

  }


  T& operator[](const int location) throw (const char *);

};


template <class T> T& MyArray<T>::operator[](const int location)

  throw (const char *) {

    if (location < 0 || location >= size) throw "Invalid array access";

    else return storage[location];

}


int main() {

  try {

     MyArray<int> x(13);

     x[0] = 45;

     x[1] = 2435;

     cout << x[0] << endl;

     cout << x[1] << endl;

     x[13] = 84;

  }

  catch (const char* e) {

    cout << e << endl;

  }

}

================================================

45

2435

Invalid array access

[c++] Overloading binary operators (C++ only)

 #include <iostream>

using namespace std;

struct X {

  // member binary operator

  void operator*(int) { 

    cout << "void operator*(int)" << endl;

  }

};

// non-member binary operator

void operator*(X, float) { 

    cout << "void operator*(X, float)" << endl;

}

int main() {

  X x;

  int y = 10;

  float z = 10;


  x * y;

  x * z;

}



void operator*(int)

void operator*(X, float)

Saturday, February 12, 2022

[c++] Overloading increment and decrement operators (C++ only)

 In the following example, the increment operator is overloaded in both ways:

=========================================================

class X {

public:

  // member postfix x++

  void operator++(int) { };

};

class Y { };

// nonmember postfix y++

void operator++(Y&, int) { };


int main() {

  X x;

  Y y;


  // calls x.operator++(0)

  // default argument of zero is supplied by compiler

  x++;

  // explicit call to member postfix x++

  x.operator++(0);

  // calls operator++(y, 0)

  y++;

  // explicit call to non-member postfix y++

  operator++(y, 0);

}

[c++] Overloading unary operators (C++ only)

An overloaded unary operator may return any type. The following example overloads the ! operator:


#include <iostream>

using namespace std;


struct X { };


void operator!(X) {

  cout << "void operator!(X)" << endl;

}


struct Y {

  void operator!() {

    cout << "void Y::operator!()" << endl;

  }

};


struct Z { };


int main() {

  X ox; Y oy; Z oz;

  !ox;

  !oy;

//  !oz;

}

  !oy;

//  !oz;

}

Friday, February 11, 2022

example code

 #include <iostream>

#include <vector>

using namespace std;


typedef struct _AppsData

{

    _AppsData(const _AppsData &) = default;

    _AppsData() = default;

    void clear() {

        cout << "AppsData clear" << endl;

        appId = "String APP ID";

    }

    int operator == (struct _AppsData & appData) {

      std::string tagAppId;

      tagAppId = appData.appId;

      cout << "_AppsData operator == appData:" << tagAppId << endl;

      return 0;

    }

    struct _AppsData & operator = (struct _AppsData & appData) {

      cout << "_AppsData operator=" << endl;

      appId = appData.appId;

      return *this;

    }

    std::string appId;

} AppsData;


typedef struct _APPInfo

{

    std::string strAppCRID;


}APPInfo, *PAPPInfo;


int test1(const std::string appid)

{

    std::vector<AppsData>::iterator cc;

    if(appid.size() == 0) {

      cout << "appid size = 0" << endl;

    }

    return 0;

}


int main()

{

   

    bool b = false;

    PAPPInfo    pAppInfo;

    AppsData appData;

    cout << "Start main" << endl;

    appData.clear();

    cout << "Do pAppInfo->strAppCRID appData.appId:" << appData.appId << endl;

    pAppInfo->strAppCRID = appData.appId;

    cout << "Start test1" << endl;

    b = !(bool)test1(std::string(pAppInfo->strAppCRID));

    cout << "end b" << b;

    return 0;

}

[c++] Overloading assignments (C++ only)

struct X {

  int data;

  X& operator=(X& a) { return a; }

  X& operator=(int a) {

    data = a;

    return *this;

  }

};


int main() {

  X x1, x2;

  x1 = x2;      // call x1.operator=(x2)

  x1 = 5;       // call x1.operator=(5)

}

==============================================================

#include <iostream>

using namespace std;


struct A {

  A& operator=(char) {

    cout << "A& A::operator=(char)" << endl;

    return *this;

  }

  virtual A& operator=(const A&) {

    cout << "A& A::operator=(const A&)" << endl;

    return *this;

  }

};


struct B : A {

    B& operator=(char) {

      cout << "B& B::operator=(char)" << endl;

      return *this;

    }

    virtual B& operator=(const A&) {

      cout << "B& B::operator=(const A&)" << endl;

      return *this;

    }

};


struct C : B { };


int main() {

  B b1;

  B b2;

  A* ap1 = &b1;

  A* ap2 = &b1;

  *ap1 = 'z';

  *ap2 = b2;


  C c1;

//  c1 = 'z';

}

A& A::operator=(char)

B& B::operator=(const A&)


The assignment *ap1 = 'z' calls A& A::operator=(char). Because this operator has not been declared virtual, the compiler chooses the function based on the type of the pointer ap1.

The assignment *ap2 = b2 calls B& B::operator=(const &A). Because this operator has been declared virtual, the compiler chooses the function based on the type of the object that the pointer ap1 points to. 

The compiler would not allow the assignment c1 = 'z' because the implicitly declared copy assignment operator declared in class C hides B& B::operator=(char).


[c++] struct和class的區別

C++中的struct是对C中的struct进行了扩充,所以增加了很多功能,主要的区别如下图所示:

cc++
成員函數不能可以
靜態成員不能可以
防控屬性預設 public,不能修改public/private/protected
繼承關係不可以繼承可以有類或其它結構體繼承
初始化不能直接初始化數據成員可以

使用過程:

在C中使用结构体时需要加上struct,或者对结构体使用typedef取别名,而C++可直接使用,例如:

================================================================

结构体声明,C和C++使用同一个

struct Student

{

int  iAgeNum;

string strName;

}

typedef struct Student Student2;//C中取别名


struct  Student  stu1; //C中正常使用

Student2   stu2; //C中通过取别名的使用

Student    stu3; //C++使用

================================================================

C与C++实际上编程思想的区别,C是面向过程,C++面向对象

所以在C中结构体就是不同类型数据的集合,并不涉及算法和操作。

而C++是把数据变量及对这些数据变量的相关算法和操作给封装起来


區別 1:内部成员变量及成员函数的默认防控属性

struct 默认防控属性是 public 的,而 class 默认的防控属性是 private 的,例如:

================================================================

struct A

{

int iNum;

}

class B

{

int iNum;

}

A a;

a.iNum = 2; //没有问题,默认防控属性为public

B b;

b.iNum = 2; //编译出错,默认防控属性为private

================================================================

區別 2:默认的继承权限

在继承关系,struct默认是public的,而class是private,例如:

================================================================

struct A

{

int   iAnum;

}

struct B : A

{

int   iBnum;

}

A a;

a.iAnum = 1; //在struct情况下是正确的,在class情况下是错误的

================================================================

struct的情况下B是默认public继承A的。如果将上面的struct改成class,那么B是private继承A的。

上面的列子都是struct继承struct,class继承class,那么class与struct继承会怎样呢?结论是:默认的防控属性取决于子类而不是基类,例如:

================================================================

struct A{};

class B : A {}; //默认为private继承

struct C : B{}; //默认为public继承

================================================================

所以我们在写代码的时候,为了不引起歧义,最好指明继承的方式,而不要用默认的继承,例如:

================================================================

class B : public A{};

struct B : public A{};

================================================================

區別 3:模板中的使用

class这个关键字还可用于定义模板参数,就像typename。但是strcut不用与定义模板参数,例如:

template<typename T> ok

template<class T> ok

template<struct T> not ok

================================================================

template< typename T, typename Y > //可以把typename 换成 class

int  Func( const T& t, const Y& y )

{

//TODO

}

================================================================

區別 4:模板中的使用

struct在C语言中:

在C语言中,我们知道struct中是一种数据类型,只能定义数据成员,不能定义函数,这是因为C语言是面向过程的,面向过程认为数据和操作是分开的,所以C语言中的struct可以直接使用大括号对所有数据成员进行初始化

================================================================

struct test

{

    int a;

    int b;

};

//初始化

test A={1,2};//完全可以

================================================================

在C++中:

在C++中对struct的功能进行了扩展,struct可以被继承,可以包含成员函数,也可以实现多态,当用大括号对其进行初始化需要注意:

当struct和class中都定义了构造函数,就不能使用大括号对其进行初始化
若没有定义构造函数,struct可以使用{ }进行初始化,而只有当class的所有数据成员及函数为public时,可以使用{ }进行初始化
所以struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。
加入一个构造函数或是一个虚函数会使strcut更体现出一种对象的特性,而使{}操作不在有效

n8n index

 【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能