友元函数与友元类

友元利弊: 友元不是类的成员但能访问类中的私有成员。友元的作用在于提高程序的运行效率,但也破坏了类的封装。 注意事项: (1)友元关系不能被继承; (2)友元关系是单向的,不具有交换性; (3)友元关系不具有传递性; 一、友元函数 类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员 尽管友元函数的原型有在类的定义中出现过,但是 友元函数并不是成员函数 this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象 友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针 1. 友元函数是全局函数 class A { friend void printA(const A &a); public: void printA(); private: string name {"C++"}; int id {1024}; }; void A::printA(){ cout << "name = " << this->name << "\tid = " << this->id << endl; } // 请注意:printA() 不是任何类的成员函数 void printA(const A &a){ cout << "name = " << a....

December 24, 2021 · 2 min · Rick Cui

构造、拷贝构造、赋值构造

结论: 拷贝构造函数是函数,赋值运算符是运算符的重载; 拷贝构造函数会生成新的类对象,赋值运算符不会; 拷贝构造函数是用一个已存在的对象去构造一个不存在的对象;而赋值运算符重载函数是用一个存在的对象去给另一个已存在并初始化过的对象进行赋值; 若接受返回值的对象已经初始化过,则会调用赋值运算符,且该对象还会调用析构函数,当对象中包含指针时,会使该指针失效,因此需要重载赋值运算符,使用类似深拷贝或移动构造函数的方法赋值,才能避免指针失效。 如果只有显示的构造函数,系统会提供默认的拷贝构造; 如果显示提供了拷贝构造,系统就不会提供默认的无参构造了,用户必需显示提供构造函数; 当既没有显式的构造函数,也没有拷贝构造时,系统才会提供默认的无参构造; 显示提供拷贝构造就必需显示提供构造函数; 显示提供赋值运算符重载就必需显示提供拷贝构造; 成员初始化列表 使用成员变量初始化列表,少了一次调用默认构造函数的过程,提高效率 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面 成员变量初始化的顺序跟在初始化列表的顺序无关,与变量声明的顺序有关 测试类 class A { public: A(){ cout << "default constructor" << endl; cout << "adrres: " << this << "\tpoint x: " << x << "\ty: " << y << endl; } A(int t){ x = new int(0); y = t; cout << "second constructor" << endl; cout << "adrres: " << this << "\tpoint x: " << x << "\ty: " << y << endl; } A(const A &a){ cout << "const copy constructor" << endl; cout << "adrres: " << this << "\tpoint x: " << x << "\ty: " << y << endl; this->x = a....

December 23, 2021 · 4 min · Rick Cui

函数重载、重写、隐藏、模板

重载: 在同一作用域中,两个函数名相同,但是参数列表不同(个数、类型、顺序),返回值类型没有要求; 重写(覆盖): 子类继承了父类,父类中的函数是虚函数,在子类中重新定义了这个虚函数,这种情况是重写或覆盖; 重定义: 派生类中函数与基类中的函数同名(形参没有要求),但是这个函数在基类中并没有被定义为虚函数 隐藏: 派生类中重定义了父类的函数,此时基类的函数会被隐藏; 模板: 函数模板是一个通用函数,函数的类型和形参不直接指定而用虚拟类型来代表,只适用于 参数个数相同而类型不同 的函数。 构造函数可以被重载,析构函数不可以被重载。因为构造函数可以有多个且可以带参数, 而析构函数只能有一个,且不能带参数 1、重载 类的静态函数也可以重载; 形参中一级指针和二级指针被认为是不同类型的参数; class A{ public: A(int a){ m_a = a; } void print(){ cout << "print()" << "m_a = " << m_a << endl; } void freeP(A ** p){ if(p == NULL){ return; } if(*p != NULL){ free(*p); *p = NULL; } } void freeP(A * p){ if(p != NULL){ free(p); p = NULL; } } static void printS(){ cout << "printS() s_a = " << s_a << endl; } static void printS(int s){ cout << "printS(int s) " << s_a * s << endl; } static void printS(void *p, int s){ cout << "printS(void *p, int s) " << ((A*)p)->m_a * s << endl; } static int s_a; private: int m_a; }; int A::s_a = 0; int main( ) { A *a = new A(1); a->printS(a, 20); a->print(); A::s_a = 10; a->printS(); a->printS(10); a->freeP(a); if(a == NULL){ cout << "*a is not valid" << endl; return 0; } cout << "*a is valid" << endl; a->print(); return 0; } 输出:...

December 23, 2021 · 2 min · Rick Cui

inline 内联函数

当一个函数被声明为内联函数之后,在编译阶段,编译器会用内联函数的函数体取替换程序中出现的内联函数调用表达式,而其他的函数都是在运行时才被替换,这其实就是用空间换时间,提高了函数调用的效率。同时,内联函数具有几个特点: 适用于函数体积很小并频繁使用的函数 内联函数中不可以出现循环、递归或开关操作 内联函数的声明必须在函数定义之前 内联函数的定义必须出现在内联函数的第一次调用前 在类中声明同时定义的成员函数(除了虚函数)会自动隐式的当成内联函数 虚函数可以是内联函数,但是当虚函数表现多态性的时候不能内联 优点: 内联函数在被调用处进行代码展开,省去了参数压栈、跳转返回、栈帧开辟与回收,结果返回等操作,从而提高程序运行速度; 内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换,而宏定义则不会; 在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能; 内联函数在运行时可调试,而宏定义不可以。 缺点: 代码膨胀,消耗了更多的内存空间; inline 函数无法随着函数库升级而升级。inline函数的改变需要重新编译,不像 non-inline 可以直接链接; 内联函数其实是不可控的,它只是对编译器的建议,是否对函数内联,决定权在于编译器; 不能对函数进行取址操作

December 23, 2021 · 1 min · Rick Cui

fork Unix

Unix 的 fork 函数 fork函数可以创建一个和当前映像一样的子进程,这个函数会返回两个值:从子进程返回0,从父进程返回子进程的PID; 1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值; #include <iostream>#include <unistd.h> using namespace std; int main() { pid_t fpid; //fpid表示fork函数返回的值 int count = 0; cout << &count << endl; fpid = fork(); if (fpid < 0) printf("error in fork!\n"); else if (fpid == 0) { printf("i am the child process, my process id is %d\n",getpid()); cout << &count << endl; count++; } else { printf("i am the parent process, my process id is %d\n",getpid()); cout << &count << endl; count++; } printf("统计结果是: %d\n",count); return 0; } 输出:...

December 23, 2021 · 1 min · Rick Cui