C++类操作相关
类的构造函数
- 构造函数与类名相同,没有返回值,参数列表可以传入多组参数,即构造函数不唯一,下面使用的是初始化成员列表,即可以在冒号后面,使用括号的方法对其进行初始化
- 注意:在定义类的时候,使用=号时,并不是赋值(
assignment
),而是调用了构造(constructor
)函数 MyClass001.h
#ifndef MYCLASS001_H #define MYCLASS001_H #include <stdio.h> #include <cstdlib> #include <iostream> #include <string> #include <set> #include <string.h> #include <map> #include <unordered_map> #include <unordered_set> #include <iostream> #include <queue> #include <list> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; class Myclass001 { public: Myclass001(int a = 0, int b = 0, int c = 0) : _a(a), _b(b), _c(c) // 相当于将a赋值给_a,以此类推 { cout << "a : " << _a << ", b :" << _b << ", c : " << _c << endl; } private: int _a; int _b; int _c; }; #endif
main.cpp
#include <stdio.h> #include "MyClass001.h" using namespace std; typedef long long ll; int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; // 4种初始化方式 Myclass001 mc1; // 如果参数列表为空,则不用加括号,表示不带参数的构造函数 Myclass001 mc2(2); Myclass001 mc3(2,3); Myclass001 mc4(2, 3, 4); // 这里即使是=的符号,也是构造函数,相当于传入一个参数进行构造 Myclass001 mc5 = 5; system("pause"); return 0; }
析构函数
在类中申请内存空间等资源时,需要在类的生命结束时,在析构函数中释放这些内存空间
MyClass02.h
#ifndef MyCLASS002_H #define MyCLASS002_H #include <stdio.h> #include <cstdlib> #include <iostream> #include <string> #include <set> #include <string.h> #include <map> #include <unordered_map> #include <unordered_set> #include <iostream> #include <queue> #include <list> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; class MyClass002 { public: MyClass002(int row, int col) : _row(row), _col(col) { cout << "apply for memory" << endl; pmat = new double[ _row * _col ]; } ~MyClass002() { cout << "free memory" << endl; delete []pmat; } private: int _row, _col; double *pmat; }; #endif
main.cpp
#include <stdio.h> #include "MyClass002.h" using namespace std; typedef long long ll; // 在函数中测试,可以看到调用析构函数时的状态 void testClass() { MyClass002 mc(3, 4); } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
类成员逐一初始化
- 对于一般所谓情况,进行类之间直接赋值时B=A,会将A中的元素逐一复制到B中,这对于一些常用的简单类型数据是没有问题的,但是如果类中含有地址等变量的话,则这种复制会造成很大的问题:两个类会共享同一块内存空间,如果一个类被析构了,这一块内存空间被释放了,则在另外一个类中继续使用是很危险的事情,因此对于有些类,需要单独写一个copy的构造函数,
- 下面首先给出一个错误的代码示例
Class002.h
#ifndef MyClass002_H #define MyClass002_H #include <stdio.h> #include <cstdlib> #include <iostream> #include <string> #include <set> #include <string.h> #include <map> #include <unordered_map> #include <unordered_set> #include <iostream> #include <queue> #include <list> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; class MyClass002 { public: MyClass002(int row, int col) : _row(row), _col(col) { cout << "apply for memory" << endl; pmat = new double[ _row * _col ]; } ~MyClass002() { cout << "free memory" << endl; delete[]pmat; pmat == NULL; } bool SetValue(int r, int c, int val) { if (r < 0 || r > _row || c < 0 || c > _col) return false; pmat[r*_col+c] = val; return true; } int getValue(int r, int c) { if (r < 0 || r > _row || c < 0 || c > _col) return INT_MIN; return pmat[r*_col + c]; } private: int _row, _col; double *pmat; }; #endif
main.cpp
#include <stdio.h> #include "MyClass002.h" using namespace std; typedef long long ll; // 这个程序无法结束,因为系统在已经释放掉的资源时出现问题,而且修改了mc1中的内容后,mc2中的变量也被修改,不符合设计要求 void testClass() { MyClass002 mc1(3, 4); MyClass002 mc2 = mc1; mc1.SetValue( 0,0,10 ); cout << mc2.getValue(0, 0) << endl; } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
在进行copy constructor时,可以在函数内访问类的private成员变量,下面给出正确的copy constructor写法
MyClass002.h
#ifndef MYCLASS002_H #define MYCLASS002_H #include <stdio.h> #include <cstdlib> #include <iostream> #include <string> #include <set> #include <string.h> #include <map> #include <unordered_map> #include <unordered_set> #include <iostream> #include <queue> #include <list> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; class MyClass002 { public: MyClass002(int row, int col) : _row(row), _col(col) { cout << "apply for memory" << endl; pmat = new double[ _row * _col ]; } // 在类中,该类的实例可以访问对应的private变量 MyClass002(const MyClass002& mc) : _row(mc._row), _col(mc._col) { cout << "copy constyructor" << endl; int cnt = _row * _col; pmat = new double[cnt]; for (int i = 0; i < cnt; i++) pmat[i] = mc.pmat[i]; } ~MyClass002() { cout << "free memory" << endl; delete[]pmat; pmat == NULL; } bool SetValue(int r, int c, int val) { if (r < 0 || r > _row || c < 0 || c > _col) return false; pmat[r*_col+c] = val; return true; } int getValue(int r, int c) { if (r < 0 || r > _row || c < 0 || c > _col) return INT_MIN; return pmat[r*_col + c]; } private: int _row, _col; double *pmat; }; #endif
main.cpp
#include "MyClass002.h" using namespace std; typedef long long ll; void testClass() { MyClass002 mc1(3, 4); // 使用类的构造函数进行初始化,会重新申请内存空间 MyClass002 mc2 = mc1; // 两块内存空间相互不干扰,程序可以正常运行 mc2.SetValue(0,0,5 ); mc1.SetValue( 0,0,10 ); cout << mc2.getValue(0, 0) << endl; } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
const修饰成员函数
- 对于不可以修改的函数参数,我们可以使用const去进行限定,但是无同时也需要确保函数中的任意一个使用了该const变量的子函数也不对其进行修改,为了使得编译器知道在调用这些函数时不会对值进行修改,需要对成员函数使用const进行修饰,相当于告诉编译器:
这个成员函数不会修改类对象的内容
- 如果类作为返回值(引用等),同时函数被const修饰了,则也需要确保将返回值限定为const
MyClass003.h
#ifndef MYCLASS003_H #define MYCLASS003_H #include <stdio.h> #include <cstdlib> #include <iostream> #include <string> #include <set> #include <string.h> #include <map> #include <unordered_map> #include <unordered_set> #include <iostream> #include <queue> #include <list> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; class MyClass003 { public: MyClass003(int a = 0, int b = 0, int c = 0) : _a(a), _b(b), _c(c) // 相当于将a赋值给_a,以此类推 { //cout << "a : " << _a << ", b :" << _b << ", c : " << _c << endl; } // int sumA(const MyClass003 &mc) { return mc.getA() + _a; } // 如果不加这个const,在上面调用时,会出错,因为编译器无法判断getA是否修改了类 int getA() const { return _a; } // 不可以修改的引用,在这里删除前面的const会报错,因为后面的const说明不对这个类进行修改 // 前面也需要设置为const来确保这一操作 const int &getARef() const { return _a; } // 可以修改的引用 // 重载上面的函数 int &getARef() { return _a; } // 因为b被标记为mutable,因此修改其值并不会破坏整个class obj的完整性 int &testMutable() const { _b++; } private: int _a; mutable int _b; int _c; }; #endif
main.cpp
#include "MyClass003.h" using namespace std; typedef long long ll; void testClass() { MyClass003 mc1(1,2,3); MyClass003 mc2(2,3,4); cout << mc1.sumA(mc2) << endl; int &a = mc1.getARef(); //因为a是mc1中_a的引用,因此修改a可以直接修改mc1._a const int &b = mc1.getARef(); a = 2; cout << mc1.getARef() << endl; } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
this与static成员变量(函数)
- static可以用于修饰那些与其他类成员变量或者函数无关的变量,在定义static成员函数时,其中不能使用non-static成员函数,但是它可以被其他non-static成员函数使用
- 注意:static成员变量与函数存在于整个应用程序的生命周期
MyClass004.h
#ifndef MYCLASS004_H #define MYCLASS004_H #include <stdio.h> #include <cstdlib> #include <iostream> #include <string> #include <set> #include <string.h> #include <map> #include <unordered_map> #include <unordered_set> #include <iostream> #include <queue> #include <list> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; class MyClass004 { public: MyClass004(int a = 0, int b = 0, int c = 0) : _a(a), _b(b), _c(c) { } // this的使用,是指向当前class obj的指针 MyClass004 copy( const MyClass004& mc ) { if (this != &mc) { this->_a = mc._a; this->_b = mc._b; this->_c = mc._c; } return *this; } static int Add(const int& a, const int& b) { return a + b; // _a = 1; //这句话有问题,因为静态函数中不能使用非静态的函数或者成员变量 } void printInfo() { cout << "a : " << _a << ", b :" << _b << ", c : " << _c << endl; } private: int _a; int _b; int _c; }; #endif
main.cpp
#include "MyClass004.h" using namespace std; typedef long long ll; void testClass() { MyClass004 mc1(1,2,3); MyClass004 mc2(4,5,6); mc2.printInfo(); MyClass004 mc3 = mc2.copy( mc1 ); mc2.printInfo(); mc3.printInfo(); cout << MyClass004::Add( 1,2 ) << endl; } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
运算符重载与友元
关于运算符重载
- 不能引入新的运算符,除
.
,*
,::
,?:
之外,其他的运算符都可以被重载 - 运算符的操作数个数不可变,比如==的操作符是2个,则重载时也必须是2个。
- 运算符的优先级不可改变
- 运算符的参数列表中,必须至少有一个参数类型为class类型,即不能为non-class类型定义其已经存在的运算符。
- 对于++运算符重载,为了避免冲突,在后缀自增时,会添加一个参数,一般为int(但是不会使用这个int)
- 不能引入新的运算符,除
关于友元
- 可以指定某个函数是一个类的友元,这个函数可以不属于任何类,也可以指定类A是类B的友元,这样就可以在类A中使用类B的private变量与函数
MyClass005.h
#ifndef MYCLASS005_H #define MYCLASS005_H #include <cstdlib> #include <string> #include <iostream> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> using namespace std; // 需要首先声明这个class的存在 class FriendClass005; class MyClass005 { // 指定FriendClass005为当前class的友元 friend FriendClass005; // 声明FriendClass005是MyClass005的友元 public: MyClass005(int a = 0) : _a(a) { } bool operator==(const MyClass005& mc) const { return mc._a == this->_a; } bool operator!=(const MyClass005& mc) const { return !(mc == *this); } // 相当于++class,前缀自增 MyClass005 operator++() { ++_a; MyClass005 tmp = *this; return *this; } // 加入1个参数,防止和前面的冲突,后缀自增 MyClass005 operator++(int) { MyClass005 tmp = *this; ++_a; return tmp; } void printInfo() { cout << "a : " << _a << endl; } private: int _a; }; class FriendClass005 { public: // 这个函数是这个类的友元,因此可以在下面定义时,直接使用其内部private变量 friend void printClass005Info(const FriendClass005& fc); // 在MyClass005的友元中使用其private变量 void disp(const MyClass005& mc) { cout << mc._a << endl; } private: int _num; }; void printClass005Info(const FriendClass005& fc) { cout << "disp a in mc's friend class : " << fc._num << endl; } #endif
main.cpp
#include "MyClass005.h" using namespace std; typedef long long ll; void testClass() { MyClass005 mc1(2); mc1.printInfo(); auto mc2 = mc1++; mc1.printInfo(); mc2.printInfo(); FriendClass005 fc; fc.disp(mc2); cout << "===auto increase test===" << endl; auto mc3 = mc1++; auto mc4 = ++mc1; mc3.printInfo(); mc4.printInfo(); } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
function object
function object
是指提供function call运算符
的class,用来解决诸如lt(val)
这样的函数调用,lt可以是函数指针,也可以是class boject,当它是class object时,会将这个操作转化为lt.operator(val)
,function call
可以接受任意个数的参数MyClass006.h
#ifndef MYCLASS006_H #define MYCLASS006_H #include <cstdlib> #include <string> #include <iostream> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> #include <vector> using namespace std; class LessThan { public: LessThan(int val) : _val(val) { nums = {1,2,3,4,5,6}; } // read base value int compVal() const { return _val; } // write base value int compVal(int nval) { _val = nval; } // 实现() 的function object bool operator() (int val) const { return val < _val; } // 实现[]的function object int operator[] (int index) const { if (index < 0 || index >= nums.size()) return -1; return nums[index]; } private: int _val; vector<int> nums; }; #endif
main.cpp
#include "MyClass006.h" using namespace std; typedef long long ll; int countLessThan(const vector<int>& nums, int val) { int cnt = 0; LessThan lt(val); for (auto num : nums) if (lt(num)) cnt++; return cnt; } void printLessThan(const vector<int>& nums, int val, ostream& os = cout) { int cnt = 0; LessThan lt(val); for (auto num : nums) if (lt(num)) cout << num << " "; cout << endl; } void testClass() { vector<int> nums = {1,10,5,4,3,6,7,2,8,9}; cout << countLessThan(nums, 5) << endl; printLessThan( nums, 5 ); LessThan lt(0); cout << lt[3] << endl; } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
重载iostream运算符
- 输入输出重载在结束时都需要返回对应的iostream
- 输入时,尽量不要将第二个class的参数设置为const,因为一般情况下我们都是需要对其进行修改的
输入与输出重载都不是成员函数,因此需要设置为友元函数,否则会出错
MyClass007.cpp
#ifndef MYCLASS007_H #define MYCLASS007_H #include <cstdlib> #include <string> #include <iostream> #include <ostream> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> #include <vector> using namespace std; class MyClass007 { public: MyClass007(int a = 0, int b = 0) :_a(a), _b(b) { } ~MyClass007() { } // 输入与输出的重载必须写成friend函数,否则编译出错,因为这个不是成员函数 friend ostream& operator<< (ostream& os, const MyClass007& mc) { os << "MyClass007, a :" << mc._a << ", b : " << mc._b << endl; return os; } // 定义输入运算符的时候,不能将mc设置为const,否则无法通过编译(如果在其中对其修改的话) friend istream& operator>> (istream& is, MyClass007& mc) { is >> mc._a >> mc._b; return is; } private: int _a; int _b; }; #endif
main.cpp
#include "MyClass007.h" using namespace std; typedef long long ll; void testClass() { MyClass007 mc1(2); cin >> mc1; cout << mc1 << endl; } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }
指向类成员函数(class member function)的指针
- 关于类成员函数的声明定义以及与普通的函数指针的区别:http://www.cnblogs.com/tracylee/archive/2012/11/15/2772176.html
- 需要使用
A.*b
或者this->.*b
去访问类成员函数 声明成员函数列表时,需要加上static,否则会出现https://blog.csdn.net/cffishappy/article/details/6821222所示的问题
MyClass008.h
#ifndef MYCLASS008_H #define MYCLASS008_H #include <cstdlib> #include <string> #include <iostream> #include <ostream> #include <assert.h> #include <time.h> #include <algorithm> #include <functional> #include <iterator> #include <fstream> #include <vector> using namespace std; class MyClass008 { public: MyClass008() { result.resize(3); _pmf = &MyClass008::square; } void square(int a) { result[0] = a*a; } void linearTwo(int a) { result[1] = a*2; } void minus(int a) { result[2] = -a; } void setType(int idx) { _pmf = func_tbs[idx]; } void setElem(int x) { (this->*_pmf)( x ); } void dispInfo() { for (int i = 0; i < result.size(); i++) cout << result[i] << " "; cout << endl; } // 使用Typedef简化类型写法 typedef void(MyClass008::*PtrType)(int); // 声明:需要使用static修饰,否则会出现 // https://blog.csdn.net/cffishappy/article/details/6821222 // 中所示的问题 static PtrType func_tbs[3]; private: vector<int> result; PtrType _pmf; }; // MyClass008::PtrType MyClass008::func_tbs[3] = { &MyClass008::square, &MyClass008::linearTwo, &MyClass008::minus }; #endif
main.cpp
#include "MyClass008.h" using namespace std; typedef long long ll; void testClass() { MyClass008 mc1; for (int i = 0; i < 3; i++) { mc1.setType(i); mc1.setElem( 4 ); mc1.dispInfo(); } } int main() { // 按照时间来初始化种子,保证每次结果不一样 srand( (int)time(0) ); //cout << rand() << endl; testClass(); system("pause"); return 0; }