C++候捷
文章目录
- 面向对象:c中函数(工具)与要操作的数据是分开的,c++中把数据与函数放在一起—类。面向对向(c++)主要的特征:继承和多态
头文件:任何一个要有防卫式声明,解决头文件重复包含问题。
1 2 3 4 5 6
#ifndef _标头_H #define _标头_H #endif
类 分两种 一种带指针的 一种不带指针的类。一般不带指针不用写析构函数。
引用问题(传4个字节):返回类型 参数类型
const问题:返回类型 函数体里的东西 参数类型
c++标准库:I/O和STL 可视化事件驱动编程的类库:MFC OWL VCL等
第一章 预先定义
内置的数据类型、语言预定义的运算符(operator)、stl中的vector和string、条件语句与循环语句、输入输出用的iostream库
- 字符常量分两类:一可打印字符(英文字母、数字、 标点符号) 二不可打印字符(’\n”\t’)
- 构造函数初始化:解决一个对象多个值同时初始化问题。默认构造函数解决对象忘记初始化问题。
- 构造函数初始化列表:1.由于构造函数不能继承,初始化列表可以可以调用基类特定的构造函数。2.引用成员和非静态const数据成员不存在赋值语义,只能初始列表。3.初始列表效率更高比函数体重初始化。
- Array和Vector:vector
*seq_addrs[seq_cnt]={&a,&b,&c} seq_cnt.size() - string:string user_name; cin>>user_name;
- const char*:字符串常量“abcdef”
- enl:它会插入一个换行符,并清除输出缓冲去区的内容。
- using namespace std;:解决类名 函数名 全局变量名 名字冲突问题。
- cerr:不带缓冲的输出,不带缓冲的话,就会每写一个字母,就输出一个字母,然后刷屏。有了缓冲,你将看到若干句子“同时”就出现在了屏幕上(由内存翻新到显存,然后刷新屏幕)
第二章 函数的设计与使用
inline函数、重载(overloaded)函数、函数template、函数指针
- 引用&:int m;int&n=m;n相当于m的别名,n就是m自己,创建同时必须初始化且是绑定关系。
- extern:多个源文件中使用。外部声明,若头文件中声明了就不用extern就用#include<>
- 全局变量:不用初始化系统自动初始化为0。局部变量:系统不会自动初始化,由程序员指定其初始值。
- 默认值:放在函数声明处,函数声明一般放在头文件,默认值放在头文件中。函数参数类型为指针类型方便指定其默认值,而引用则不行。
- inline:必须与函数定义放在一起才起作用。
函数指针:指明函数的返回类型及参数列表。本质是一个指针变量,指向函数的指针。int (*fp)(int,int);函数指针还允许将函数作为参数传递给其他函数,也就是回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#include <iostream> using namespace std; int add(int, int); int sub(int, int); int(*fp)(int, int); int main() { fp = add;//等价于 fp=&add;且在类中普通成员函数和虚拟成员函数必须这样的形式绑定到具体的对象。 /*fp = sub;*/ cout << (*fp)(1, 2) << endl;//等价于 cout << fp(1, 2) << endl; fp = sub; cout << (*fp)(3, 4) << endl; return 0; } int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; }
函数指针数组:int (*fp[])(int,int);
数组指针:int (*fp)[];
指针函数:本质是一个函数,函数的返回类型是指针类型返回的是指针(地址)。int* fp(int,int)
指针数组:int* fp[];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
//2.2.cpp #include <iostream> #include <vector> #include <string> using namespace std; //bool calc_elements(vector<int>&vec, int pos); void display_elems(vector<int>&vec, const string&title, ostream&os = cout); extern void really_calc_elems(vector<int> &, int); inline bool calc_elems(vector<int> &, int); int main() { vector<int> pent; const string title("pdfd"); if (calc_elems(pent, 0)) display_elems(pent, title); if (calc_elems(pent, 8)) display_elems(pent, title); if (calc_elems(pent, 14)) display_elems(pent, title); if (calc_elems(pent, 139)) display_elems(pent, title); } //bool calc_elements(vector<int>&vec, int pos) //{ // if (pos <= 0 || pos > 64) // { // cerr << "Sorry,invalid position:" // << pos << endl; // return false; // } // for (int ix = vec.size() + 1; ix <= pos; ++ix) // { // vec.push_back(ix*(3 * ix - 1) / 2); // // } // return true; //} void display_elems(vector<int>&vec, const string & title, ostream &os) { os << '\n' << title << "\n\t"; for (int ix = 0; ix < vec.size(); ++ix) os << vec[ix] << ' '; os << endl; } inline bool calc_elems(vector<int> &vec, int pos) { if (pos <= 0 || pos > 64) { cerr << "sorry" << pos << endl; return false; } if (vec.size() < pos) really_calc_elems(vec, pos); return true; } //2.3.cpp #include <iostream> #include <vector> #include <string> using namespace std; void really_calc_elems(vector<int>&vec, int pos) { for (int ix = vec.size() + 1; ix <= pos; ++ix) { vec.push_back(ix*(3 * ix - 1) / 2); } }
第三章 STL
一组容器类(vector、list、set、map)、一组泛型算法(操作容器的)(sort()、copy()、merge())
- 容器分类:顺序性容器(vector、list)关联性容器(set、map,可以快速查找容器中的元素)
- vector到array到list
- (list指针相互连接 )泛型指针(Iterator迭代器):
- (赋予运算符不同意义)函数指针:
(赋予运算符不同意义)函数对象(function object也称仿函数):即它们是行为类似函数的对象,本质是个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include <iostream> using namespace std; class CAverage//类 { public: double operator()(int a1, int a2, int a3) { //重载()运算符 return (double)(a1 + a2 + a3) / 3; } }; int main() { CAverage average; //能够求三个整数平均数的函数对象 cout << average(3, 2, 3); //等价于 cout << average.operator(3, 2, 3); return 0; }
运算符重载技术:
第四章 class机制的设计与使用过程
- 函数的重载、内联 、const、virtual四种机制:其中const和virtual 仅仅用于类的成员函数。
- 重载:一用于类的构造函数,因为构造函数函数名必须与类同名,二用于运算符重载。全局函数与成员函数同名不算重载,因为一个是文件作用域一个是类作用域,若要使用全局函数需要加 一元作用域解析运算符(::)。注意隐式类型转换可能导致重载二义性。
覆盖:是指派生类重新实现基类成员函数 函数名 参数相同 定义不同。且必须有virtual,才能被使用才有意义。
隐藏:一是指派生类重新实现基类成员函数 函数名 参数不相同 定义不同,基类函数在派生类中被隐藏。二是指派生类重新实现基类成员函数 函数名 参数相同 定义不同 且必须无virtual,基类函数在派生类中被隐藏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#include <iostream> using namespace std; class base { public: void test(int x) { cout << "基类" << x << '\n'; } private: protected: }; class child :public base { public: /*using base::test;*///使从基类继承下的test()函数不隐藏 using声明 void test(char * y) { cout << "派生类" << y << '\n'; } //void test(int x) //{ // cout << "基类2" << x << '\n'; //} void test(int x) //使从基类继承下的test()函数不隐藏 调用传递 { base::test(x); } private: protected: }; int main() { child *r = new child(); r->test(5); return 0; }
参数的默认值:必须放在函数的声明中。且只能从后向前依次默认。.
运算符重载:一重载为类的成员函数,二元运算符只有一个右侧参数,因为有this指针隐含有的。二重载为全局函数,二元运算符有两个参数。
inline函数内联:省去了函数调用的开销,但函数体内出现循环不用内联 因为会造成代码膨胀还不如函数调用的开销小。
this指针的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include <iostream> using namespace std; class object { public: int a; int b; void xianshi()//省略了object* this参数。 { cout << "a与b的值分别为:"<<a <<"、"<<b << endl;//省略了this->(this->a,this->b) } void add() { cout << "a与b之和为:"<<a+b<< endl;//省略了this->(this->a+this->b) cout << endl; } protected: private: }; int main() { object obj1; obj1.a = 1; obj1.b = 2; cout << "obj1:" << endl; obj1.xianshi(); obj1.add(); object obj2; obj2.a = 3; obj2.b = 4; cout << "obj2:" << endl; obj2.xianshi(); obj2.add(); object obj3; obj3.a = 8; obj3.b = 5; cout << "obj3:" << endl; obj3.xianshi(); obj3.add(); cout << obj1.a<<endl;//这种方式不好 ,最好用函数(工具)去显示。 return 0; }
类型转换函数:本质是创建新的目标对象,并以源对象的值来初始化,初始化过程中调用了operator=()完成拷贝赋值。可以把带有一个参数的构造函数看作一个类型转换函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include<iostream> using namespace std; class fenshu { public: fenshu() { } fenshu(double fenzi, double fenmu) :z(fenzi), m(fenmu) { } fenshu operator +(const fenshu&other)//重载加号+操作让分数类的对象可以像普通类型的变量一样加操作 { fenshu result; result.z = z*other.m + m*other.z; result.m = m*other.m; return result; } operator double()const//让分数类型的(默认)this对象转为double类型的对象进行直接输出操作 { return z / m; } //void shuchu() //{ // cout << z <<'/'<< m << endl; //} // private: double z; double m; }; int main() { fenshu no1(3, 2); fenshu no2(3, 5); fenshu no3 = no1 + no2; cout << no3 << endl; //no1.shuchu(); return 0; }
拷贝构造:等号左边对象不存在 需要创建的同时并用另一个已经存在的对象来初始化。
拷贝赋值:等号左边对象已经存在 并用另一个已经存在的对象赋值给左边已经存在的对象。
浅拷贝与深拷贝:浅:只是对地址(指针)的复制 深:复制具体的内存中存的值(复制内存)当有指针成员时要进行深拷贝。深拷贝解决方式:1.自定义拷贝构造函数2.自定义拷贝赋值函数 C++之浅拷贝和深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
#include <iostream> using namespace std; class object { public: object() { cout << "用户自己写的默认构造函数" << endl; } object(int v) :value(v) { cout << "构造对象" << endl; } object(const object& other) { this->value = other.value; cout << "拷贝构造对象" << endl; } object& operator=(const object& other) { this->value = other.value; cout << "拷贝赋值" << endl; return *this; } void Test() { cout << "类的成员函数" << endl; } object test() { object d; cout << "有返回类型的函数" << endl; return d; } void test(object x) { cout << "有参函数" << endl; } void Test3(object &test) {} object &Test4() { object *pA = new object; return *pA; } protected: private: int value; }; void Test(object obj) { cout << "全局函数" << endl; } int main() { object a(123);//调用自己写的含参构造函数 object b(a);//等价于object b=a;创建对象且用另一个已经存在对象初始化时 object *p = new object(a);//new 动态创建对象时 Test(a);//函数传参时 调用拷贝构造函数和全局函数 b = a;//调用拷贝赋值 object c;//调用用户自己写的默认构造函数 c.Test();//调用类的成员函数 cout << endl; object e;//调用用户自己写的默认构造函数 e = c.test();/* 函数返回值时,调用复制构造函数;将返回值赋值给obj2时,调用重载赋值操作符 * 函数返回值时,也会构造一个临时对象;调用复制构造函数将返回值复制到临时对象上 */ cout << endl; c.test(a);/* 传参的过程中,要调用一次复制构造函数 * obj1入栈时会调用复制构造函数创建一个临时对象,与函数内的局部变量具有相同的作用域 */ cout << endl; c.Test3(e); // 参数是引用,没有调用复制构造函数 object obj3; c.Test4(); // 返回值是引用,没有调用复制构造函数 return 0; }
第五章 class形成层次体系(继承)
- 继承:protectedz只能被子类继承。classB:public A{} public从父类继承来的public和protected成员属性不变, protect从父类继承来的public和protected成员属性降低为protected,private从父类继承来的public和protected成员属性降低为private. 继承不能继承构造函数、析构函数、赋值函数。
多态(虚函数):在子类中成员函数重写,父类指针指向子类对象 该对象访问函数时访问的是父类的函数不是子类中重写的函数 这时引入虚函数便解决了问题。此时基类是多态类析构函数也要定义为虚函数,以免造成内存泄漏。基类(干脆不定义反正派生类要重新对函数定义)直接虚函数等于零形成纯虚函数,亦形成纯虚类也叫抽象基类也叫接口类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include <iostream> using namespace std; class base { public: virtual void test()//此处加virtual 后输出结果为new右边的类型 派生类 基类 派生类 不加时输出为基类 基类 派生类 { cout << "基类" << '\n'; } private: protected: }; class child :public base { public: void test() { cout << "派生类" << '\n'; } private: protected: }; int main() { base *p = new child(); p->test(); base *q = new base(); q->test(); child *r = new child(); r->test(); //child *s = new base(); //初始化无法从base转换为child //s->test(); return 0; }
第六章 (binary tree)class template
函数模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> using namespace std; template<typename T> T findmax(T arr[],int len) { T value = arr[0]; for (int i=0;i<len;++i) { if (arr[i] > value) { value = arr[i]; } } return value; } int main() { int a[5] = {5,4,6,1,4}; int result = findmax<int>(a, 5); cout << result << endl; return 0; } |
类模板:函数声明与实现(定义)写在一个地方。
第七章 c++的异常处理机制
异常处理:抛出异常、提炼异常、捕获异常、异常对象本身。throw 、try{}、catch{} throw是一条语句,只能抛出一个异常。try{}包含可能会出现异常的代码段。catch{}用户定义的异常处理器,只能处理一个异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include <iostream> #include <string> using namespace std; class CException { public: string msg; CException(string s) : msg(s) {} }; double Devide(double x, double y) { if (y == 0) throw CException("devided by zero"); cout << "in Devide" << endl; return x / y; } int CountTax(int salary) { try { if (salary < 0) throw - 1; cout << "counting tax" << endl; } catch (int) { cout << "salary < 0" << endl; } cout << "tax counted" << endl; return salary * 0.15; } int main() { double f = 1.2; try { CountTax(-1); f = Devide(3, 0); cout << "end of try block" << endl; } catch (CException e) { cout << e.msg << endl; } cout << "f = " << f << endl; cout << "finished" << endl; return 0; } |
待解决问题:
1.实现一次深度拷贝。
2.不用递归用循环的方式实现一次 汉诺塔。
3.纯手工 不看资料手写一些基础的数据结构与算法。
4.判断是素数且是回文数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <iostream> #include <cmath> using namespace std; bool is_primer(int val) { if (val == 1) return false; for (int i = 2; i <= sqrt(val); ++i) { if (val % i == 0) return false; } return true; } bool is_textloop(int val) { int m; int sum = 0; m = val; while (m) { sum = sum * 10 + m % 10; m /= 10; } if (sum == val) return true; else return false; } int main() { int lowbound, upbound; int count = 0; cin >> lowbound >> upbound; for (int i = lowbound; i <= upbound; ++i) if (is_primer(i)) if (is_textloop(i)) { ++count; } cout << count << endl; return 0; } |
基于对象
class不带指针的 complex(复数类)
class带指针的 string(字符类)
面向对象
- 组合:一个类拥有一个别的不带指针的类 利用别的类来完成自己的事 拥有了一个东西
- 委托:一个类拥有一个别的带指针的类 利用别的类来完成自己的事 只是指向了另一东西
- 继承:纯虚函数必须在子类中重新写,空函数子类中不重新写也不会出错。
泛型编程与模板编程兼谈对象
- explicit用在构造函数前面。可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。
- 智能指针 泛型指针 迭代器
- 函数对象
文章作者 周军
上次更新 2019-09-27