情景1,通常情况下,我们将2个数据类型为结构体类型的2个数相加时,所书写的函数。
代码1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std; struct Complex { float real; float image; }; Complex add(Complex a,Complex b) { Complex c; c.image = a.image + b.image; c.real = a.real + b.real; return c; } int main() { Complex aa,bb,cc; aa = {1,1}; bb = {2,2}; cc = add(aa,bb); cout<<"cc.image = "<<cc.image<<endl; cout<<"cc.real = "<<cc.real<<endl; return 0; }
运行结果:
1 2 cc.image = 3 cc.real = 3
情景2,我们可以通过使用操作符重载技术,来实现,像这种结构体类型的数据,可以按照普通的数据类型(char,int ,double……)进行操作。
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 #include <iostream> using namespace std; struct Complex { float real; float image; }; Complex operator+(Complex a,Complex b) { Complex c; c.real = a.real + b.real; c.image = a.image + b.image; return c; } int main() { Complex aa,bb,cc; aa = {1,1}; bb = {2,2}; cc = aa+bb; cout<<"cc.real = "<<cc.real<<endl; cout<<"cc.image = "<<cc.image<<endl; return 0; }
运行结果:
1 2 cc.real = 3 cc.image = 3
功能:设置一个简易的电子时钟,可以显示时分秒
技巧:setfill函数,在给定的输出域宽内填充字符; setw函数,设置域宽为n个字符
源码:
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 #include <iostream> #include <iomanip> #include <windows.h> #include <unistd.h> using namespace std; int main() { int hour = 0,min = 0,sec = 0; while(1) { sec++; sleep(1); if(sec>=60) { sec = 0; min++; if(min>=60) { min = 0; hour++; if(hour >= 24) { hour = 0; } } } cout<<setfill('0')<<setw(2)<<hour<<":"<<setw(2)<<min<<":"<<setw(2)<<sec<<endl; } return 0; }
总结:
setfill函数和setw函数,这2个函数结合起来使用,用来制作一个简易的电子时钟还是蛮不错的。
代码1:
特征:函数重载反映在,函数的返回值和函数的返回参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std; float abs(float f); int abs(int i); int main() { float ret_f = abs(-5.5f); cout<<ret_f<<endl; int ret_i = abs(-5); cout<<ret_i<<endl; return 0; } float abs(float f) { return ((f>0)?(f):(-f)); } int abs(int i) { return ((i>0)?(i):(-i)); }
运行结果:
代码2:
特征:函数重载反映在,函数的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> using namespace std; void abs(int i); void abs(double d); int main() { abs((double)4); return 0; } void abs(int i) { cout<<"int abs(i)"<<endl; } void abs(double d) { cout<<"double abs(d)"<<endl; }
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> #include <time.h> using namespace std; int volume(int l,int w,int h=6) { return l*w*h; } void weatherCast(string w = "pm 2.5") { time_t t = time(0); char tmp[64]; strftime(tmp,sizeof(tmp),"%Y-%m-%d %X %An",localtime(&t)); cout<<tmp<<"today's weather "<<w<<endl; } int main() { cout<<"volume = "<<volume(1,1)<<endl; weatherCast("pm = 555"); return 0; }
运行结果:
1 2 3 volume = 6 2018-11-08 16:36:18 Thursday today's weather pm = 555
总结:
默认参数,当我们不给它任何的参数时,它就按照形参中实现指定的值。
当,我们想要设定特定的参数时,那就直接给它传入相应的值即可。
代码实战2,
默认参数的存在,的确在一定程度上给我们提供了不少的便利性。然而,它也有它的囧态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> using namespace std; int abs(int a) { cout<<"int abs(int a)"<<endl; } int abs(int a,int b=5) { cout<<"int abs(int a,int b=5)"<<endl; } int main() { abs(1,5);//假如写成:abs(1),此时报错。 return 0; }
总结,
当我们写为abs(1),里面仅有一个参数时,函数会陷入两难的境地,它不知道要执行abs(int a)还是要执行
abs(int a,int b=5)。
引用的定义:引用的意义,是为了给一个已有的变量名起一个别名。
代码实战1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> using namespace std; int main() { int a; int &b = a; int *p; int *&pp = p; cout<<"&a = "<<&a<<endl; cout<<"&b = "<<&b<<endl; cout<<"p = "<<p<<endl; cout<<"pp = "<<pp<<endl; return 0; }
运行结果:
1 2 3 4 &a = 0x28fea4 &b = 0x28fea4 p = 0x68 pp = 0x68
在C语言中,打印一个数字。分别使用十进制、十六进制、八进制的形式打印。
代码如下:
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main() { int a = 128; printf("dec: a = %dn",a); printf("hex: a = %xn",a); printf("oct: a = %on",a); return 0; }
运行结果:
1 2 3 dec: a = 128 hex: a = 80 oct: a = 200
在C++中,打印一个数字。分别使用十进制、十六进制、八进制的形式打印。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> #include <stdlib.h> #include <iomanip> using namespace std; int main() { int a = 1234; cout<<dec<<a<<endl; cout<<setbase(10)<<a<<endl; cout<<"0x"<<hex<<a<<endl; cout<<"0x"<<setbase(16)<<a<<endl; cout<<oct<<a<<endl; cout<<setbase(8)<<a<<endl; }
运行结果:
1 2 3 4 5 6 1234 1234 0x4d2 0x4d2 2322 2322
总结,dec等价于setbase(10);hex等价于setbase(16);oct等价于setbase(8)。
1 2 3 4 5 6 7 8 情景1: const int a = 1234; int &b = a; 该使用方法是错误的。 情景2: int a = 1234; const int &b = a; 该使用方法是OK,正确的。
情景1:
1 2 3 4 int *p = new int(123); cout<<*p<<endl; 运行结果: 123
情景2:
1 2 3 4 5 6 7 8 9 10 11 12 char *p = new char [10]; strcpy(p,"hello world"); cout<<p<<endl; 运行结果: hello world string *str = new string("china"); cout<<str<<endl; cout<<*str<<endl; 运行结果: 0x3c1518 china
static_cast
它用来解决全双隐问题和半双隐问题。
全双隐问题(a.b两种数据类型的变量,a既可以赋值给b,b又可以赋值给a)
半双隐问题(a,b两种类型的变量,a可以赋值给b,b不可以赋值给a;或者…..)
下面先看一下static_cast使用在什么地方呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1. 全双隐问题: int a; float b; a = b; b = a; 首先,上面这4行代码是正确的。在这种情形之下,恰好也就是static_cast的用武之地了。 如下, int a = 123; float b = 1.23; a = static_cast<float>(b); b = static_cast<int>(a); cout<<a<<"t"<<b<<endl; 运行结果: 1 1 2. 半双隐问题: int *p; void *q; q = p;//这样写是正确的。但是,p = q,这样写的话,就是错误的。 p = static_cast<int *>(q); // 这样写也是正确的。
reinterpret_cast
它是用来解决,2个方向都是不可以转换的。
1 2 3 4 5 6 7 char *p; int *q; 此时,无论是p = q;还是q = p都是不正确的。 然而, p = reinterpret_cast<char *>(q); q = reinterpret_cast<int *>(p); 这2种情况都是正确的。
const_cast
它是用来脱常的。(正常情况下,引用是只可以对变量进行取引用的。但是,有了const的介入,就导致了引用,它不止可以对变量取引用。而且它还可以对常量或者表达式取引用。)
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 情景1, 谈一谈const的妙用之处。通常情况下,引用只可以对一个变量,进行取引用操作。而,自从有了const关键字之后,它,所实现的功能:取引用,即可以对常量取引用;也可以对表达式进行取引用操作。 void func_1(int &a) { } main: int a = 3; func_1(a); 以上代码是完全没有毛病的。这也是引用的一般、正常的使用方法。 void func_2(const int &a) { } main: int a = 3; func_1(3); func_1(a+3); //这2个调用,在没使用const之前都是行不通的。但是,在这里都是OK的。 情景2, 正式来讲一下const_cast是如何使用的。 void func(int &a) { } main: const int a = 3; func(a); //此时,这样调用函数,一定是错误的。 func(const_cast<int &>(a)); //把原本为const类型的变量,给,成功的"脱常"。 情景3, const int a = 12; int &ra = a; //此时,编译报错。 使用const_cast即可解决上述问题。 int &ra = const_cast<int &>(a); 情景4, const int a = 10; int *p = const_cast<int *>(&a); *p = 30; cout<<"*p = "<<*p<<"t"<<"a = "<<a<<endl; 注意: const_cast一般只用于引用和指针,是不可以用于变量的。具体,const_cast<int &> const_cast<int *>...
定义:命名空间是对全局区域的再次划分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 情景1, int val = 200; main: int val = 100; cout<<"::val = "<<::val<<endl; cout<<"val = "<<val<<endl; 运行结果: ::val = 200 val = 100 情景2, namespace my_space { int x=5,y=5; namespace others { int m = 6,n = 6; } } main: using namespace my_space::others; cout<<"m = "<<m<<"t"<<"n = "<<n<<endl; 运行结果: m = 6 n = 6
类与对象:
构造器:在类对象创建时,自动调用。
析造器:在对象销毁时,自动调用。
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 class Line { public: void setLength(double len); double getLength(void); Line(); ~Line(); private: double length; }; //构造器 Line::Line(void) { cout<<"Object is being created"<<endl; } //析造器 Line::~Line(void) { cout<<"Object is being deleted"<<endl; } void Line::setLength(double len) { length = len; } double Line::getLength() { return length; } main: Line line; line.setLength(6.66); cout<<"length of line :"<<line.getLength()<<endl; 运行结果: Object is being created length of line :6.66 Object is being deleted
使用初始化列表来初始化参数。
注意它使用的场景,适用于构造器身上。
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 情景1, class Line { public: void setLength(double len); double getLength(void); Line(double len); ~Line(); private: double length; }; //使用初始化列表,来初始化参数。 Line::Line(double len) :length(len) { cout<<"Object is being created,length = "<<len<<endl; } Line::~Line(void) { cout<<"Object is being deleted"<<endl; } main: Line line(1.23); //注意这里的对象,在初始化时,方式和前面是不一样的。 运行结果: Object is being created,length = 1.23 Object is being deleted 情景2, class A { public: A(char *ps) :len(strlen(name.c_str())),name(ps){} //c_str()将string类型转化为char *类型的 void dis() { cout<<len<<endl; } private: string name; int len; }; main: A a("a"); a.dis(); 注意: 使用初始化列表初始化参数时,一定要注意,初始化的先后顺序。
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< 大专栏 C++杂记 /span>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 拷贝构造的格式: class 类名 { 类名(const 类名 &another) 拷贝构造体 } 情景1,浅拷贝。(默认的) class A { public: A(int d,char *p) :data(d) { pd = new char[strlen(p)+1]; strcpy(pd,p); } ~A() { delete []pd; } #if 0 // 这是深拷贝时,自己手动创建的拷贝构造器。 A(const A &another) { pd = new char[strlen(another.pd)+1]; strcpy(pd,another.pd); } #endif void dis() { cout<<data<<"t"<<pd<<endl; } private: int data; char *pd; }; main: A a(1,"a"); a.dis(); A b(a); b.dis(); 运行结果: 1 a 1 a 情景2,深拷贝。(需要自己提供) 见情景1,中的代码。加上,"#if 0...#endif"自己手动添加的拷贝构造器,之后,运行结果如下。 1 a 104 a 情景3,深拷贝。 class A { public: A(int d,char *p) :data(d) { pd = new char[strlen(p)+1]; strcpy(pd,p); } ~A() { delete []pd; } A(const A& another) { pd = new char[strlen(another.pd)+1]; strcpy(pd,another.pd); } void dis() { cout<<data<<"t"<<pd<<endl; } private: int data; char *pd; }; main: A a(123,"abc"); A b = a; a.dis(); b.dis(); 运行结果: 123 abc 104 abc
定义:系统在创建对象时,默认生成指向当前对象的指针。
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 class Stu { public: Stu(string name,int age) { this->name = name; this->age = age; } Stu &growUp() { this->age++; return *this; } void display() { cout<<name<<"--"<<age<<endl; } private: string name; int age; }; main: Stu s("lbc",25); s.display(); s.growUp(); s.display(); 运行结果: lbc--25 lbc--26
构造器、拷贝构造、赋值运算符重载,这3者放一块,做一个比较。
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 class my_str { public: my_str(){} //构造器,带参数的 my_str(int _id,char *_name) { cout<<"constructor"<<endl; id = _id; name = new char[strlen(_name)+1]; strcpy(name,_name); } //拷贝构造 my_str(const my_str &str) { cout<<"copy constructor"<<endl; id = _id; name = new char[strlen(str.name)+1]; strcpy(name,str.name); } //赋值运算符重载 my_str &operator=(const my_str &str) { cout<<"operator = "<<endl; if(this != &str) { this->id = str.id; int len = strlen(str.name); name = new char[len+1]; strcpy(name,str.name); } return *this; } ~my_str() { delete name; } private: char *name; int id; }; main: my_str str1(123,"abc"); cout<<"------------------"<<endl; my_str str2; str2 = str1; //这里是,赋值运算符重载 cout<<"------------------"<<endl; my_str str3 = str2; //这里是,拷贝构造 运行结果: constructor ------------------ operator = ------------------ copy constructor
注意: 深拷贝也可以称之为拷贝构造。
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 class A { public: A():x(12),y(34){} void dis() { cout<<"void dis()"<<endl; cout<<x<<"t"<<y<<endl; } void dis() const { cout<<"void dis() const"<<endl; cout<<x<<"t"<<y<<endl; } private: int x,y; }; main: A a; a.dis(); 运行结果: void dis() 12 34 总结: const关键字修饰函数时,是可以构成函数重载的。此时,优先执行,非const修饰的函数。
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 情景1, 点1:static修饰的类成员,不需要类对象,也是可以对其成员进行访问的。 点2:要想使用static类型的成员,必须先要对其初始化。而且,这个初始化的过程还必须在类外,进行初始化 class School { public: static void add_lib_book(string book) { lib = lib + book; } public: string tower; string lake; static string lib; }; string School::lib = "jx lib"; main: School::lib = "jx lib"; cout<<School::lib<<endl; School::add_lib_book(" good book"); cout<<School::lib<<endl; 运行结果: jx lib jx lib good book 情景2, static函数存在的意义在于,用来管理类中的静态成员变量。 class Student { public: Student(int n,int a,float s) :num(n),age(a),score(s){} static float average(); void total() { count++; sum = sum + score; } private: int num; int age; float score; static float sum; static int count; }; float Student::sum = 0; int Student::count = 0; float Student::average() { return sum/count; } main: Student stu[3] = { Student(100,10,1), Student(200,20,2), Student(300,30,3) }; for(int i=0;i<3;i++) { stu[i].total(); cout<<Student::average()<<endl; } 运行结果: 1 1.5 2
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 实战1,this指向普通函数 void dis(int cnt) { cout<<"cnt = " <<cnt<<endl; } main: void (*pf)(int) = dis; pf(10) 运行结果: cnt = 10 实战2,this指向类成员函数 class Student { public: Student(string n,int a) :name(n),age(a){} void dis() { cout<<"name: "<<name<<"t"<<"age: "<<age<<endl; } string name; int age; }; main: Student s("lbc",120); void (Student::*pf)() = Student::dis;//这里要注意,函数指针书写的格式。 (s.*pf)(); 运行结果: name: lbc age: 120
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 情景1, class Point { public: Point(float xx,float yy) :x(xx),y(yy){} void get_point(); friend float cal_distance(Point &a,Point &b); private: float x,y; }; void Point::get_point() { cout<<"("<<x<<","<<y<<")"<<endl; } float cal_distance(Point &a,Point &b) { float d_x = a.x - b.x; float d_y = a.y - b.y; return sqrt(d_x*d_x - d_y*d_y); } main: Point p1(1.1,2.2); Point p2(2.2,2.2); p1.get_point(); p2.get_point(); float res = cal_distance(p1,p2); cout<<"res = "<<res<<endl; 运行结果: (1.1,2.2) (2.2,2.2) res = 1.1 情景2, class Point; class Cal_distance { public: float distance(Point &a,Point &b); }; class Point { public: Point(float xx,float yy) :x(xx),y(yy){} void get_point(); friend float Cal_distance::distance(Point &a,Point &b); private: float x; float y; }; void Point::get_point() { cout<<"("<<x<<","<<y<<")"<<endl; } float Cal_distance::distance(Point &a,Point &b) { float d_x = a.x - b.x; float d_y = a.y - b.y; return sqrt(d_x*d_x + d_y*d_y); } main: Point p1(1.1,2.2); Point p2(1.1,3.3); p1.get_point(); p2.get_point(); Cal_distance cd; float res = cd.distance(p1,p2); cout<<"res = "<<res<<endl; 运行结果: (1.1,2.2) (1.1,3.3) res = 1.1 总结: 友元函数friend函数,从目前来看它,的不同之处在于,它可以访问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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 情景1,成员函数重载 class Complex { public: Complex(float xx=0,float yy=0) :x(xx),y(yy){} void dis(); Complex operator+(const Complex &another); private: float x,y; }; void Complex::dis() { cout<<"x="<<x<<"t"<<"y="<<y<<endl; } Complex Complex::operator+(const Complex &another) { return Complex(this->x+another.x,this->y+another.y); } main: Complex p1(1.1,2.2); Complex p2(1.1,0.0); p1.dis(); p2.dis(); Complex p3 = p1+p2; p3.dis(); 运行结果 : x=1.1 y=2.2 x=1.1 y=0 x=2.2 y=2.2 情景2,友元函数重载 class Complex { public: Complex(float xx=0,float yy=0) :x(xx),y(yy){} void dis(); friend Complex operator+(Complex &a,Complex &b); private: float x,y; }; void Complex::dis() { cout<<"x="<<x<<"t"<<"y="<<y<<endl; } Complex operator+(Complex &a,Complex &b) { return Complex(a.x+b.x,a.y+b.y); } main: Complex p1(1.1,2.2); Complex p2(1.1,0.0); p1.dis(); p2.dis(); Complex p3 = p1+p2; p3.dis(); 运行结果: x=1.1 y=2.2 x,1.1 y=0 x=2.2 y=2.2
继承Inherit
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 情景1, class People { public: void eat(string food) { cout<<"I am eating : "<<food<<endl; } }; class Student :public People { public: void study(string subject) { cout<<"I am studying : "<<subject<<endl; } }; class Teacher :public People { public: void teach(string subject) { cout<<"I am teaching : "<<subject<<endl; } }; main: Student s; Teacher t; s.eat("Student"); s.study("China"); cout<<"------------------"<<endl; t.eat("Teacher"); t.teach("English"); 运行结果: I am eating : Student I am studying : China ------------------ I am eating : Teacher I am teaching : English 情景2,看一下继承与虚继承之间的差异。 class Base { public: Base(int d = 10) :data(d){} void dis() { cout<<"Base data = "<<data<<endl; } int data; }; class A :virtual public Base { public: A() { cout<<"A() Base data = "<<data<<endl; } void set_data(int d) { data = d; } }; class B:virtual public Base { public: B() { cout<<"B() Base data = "<<data<<endl; } int get_data() { return data; } }; class C:public A,public B { public: C() { cout<<"C() Base data = "<<data<<endl; } void dis() { cout<<"C() data = "<<data<<endl; cout<<"A() data = "<<data<<endl; cout<<"B() data = "<<data<<endl; } }; main: C c; c.dis(); 运行结果: A() Base data = 10 B() Base data = 10 C() Base data = 10 C() data = 10 A() data = 10 B() data = 10 总结,这里假如没有virtual修饰的话,class C中想要输出data的值是,会出现"data is ambiguous"的这种关键字样。这个data,它,不知道应该从class A,还是从class B中获取的。