c++ 命名 文件名结尾为.cpp
编译 使用g++
编译
hello world 1 2 3 4 5 6 7 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { cout << "hello world! 你好,世界!" << endl ; return 0 ; }
主函数必须要返回int
endl
会立即刷新缓冲区实现换行,\n
也可以实现换行,但没有刷新缓冲区的作用,所以无法保证立即输出到屏幕上,只有在执行后面的语句时才会写到屏幕上
cout
为一个对象
cin 1 2 3 4 5 6 7 8 9 10 11 #include <iostream> using namespace std ;int main (void ) { int age; char name[12 ]; cout << "输入姓名,年龄:" ; cin >> name >> age; cout << "姓名:" << name << endl << "年龄:" << age << endl ; }
cin
为一个对象,接收标准输入
namespace c++中需要使用命名空间来区分不同的函数名,变量名等。例如cin,cout
输入std
这个命名空间
使用namespace的几种方式:
1 2 3 4 using namespace std ;count << "hello" ;
1 2 std ::cout << "hello" << std ::endl ;
1 2 3 4 5 using std ::cout ;using std ::endl ;cout << "hello" << endl ;
总之这些写法都和perl中的使用很相似
struct c++中,struct在使用的时候不需要写struct(c语言中声明与使用都需要)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std ;struct Student { char name[15 ]; unsigned int age; }; int main (int argc, char **argv) { Student stu1 = {"昊天" , 22 }; stu1.age = 24 ; cout << stu1.name << stu1.age << endl ; }
new/delete 空间的申请与释放不再用c语言的malloc与free,而是使用new与delete
1 2 3 int *age = new int ;*age = 20 ; delete age;
申请时并赋值
申请数组与释放
1 2 3 int *list = new list [5 ];memset (list , 0 , sizeof (int )*5 );delete [] list ;
reference c++中多了引用,之前学perl的时候觉得引用和指针是一样的,但是现在看来这两个存在本质上的区别,引用是多个变量指向同一个地址。指针就是内存地址,引用是对同一块内存地址的不同命名。
1 2 3 4 5 6 7 8 9 10 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { int a = 1 ; int &b = a; cout << a << endl << b << endl ; }
1 2 3 4 5 6 7 8 9 10 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { char a[10 ] = "hello" ; char (&b)[10 ] = a; cout << a << endl << b << endl ; }
1 2 3 4 5 6 7 8 9 10 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { const int a = 1 ; const int &b = a; cout << a << endl << b << endl ; }
1 2 3 4 5 6 7 8 9 10 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { int a[10 ]; int (&b)[10 ] = a; cout << a[0 ] << endl << b[9 ] << endl ; }
1 2 3 4 5 6 7 8 9 10 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { int a[10 ][2 ] = {10 }; int (&b)[10 ][2 ] = a; cout << a[0 ][1 ] << endl << b[9 ][0 ] << endl ; }
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> using namespace std ;int main (int argc, char *argv[]) { int a = 1 ; int *p1 = &a; int *(&p2) = p1; *p2 = 4 ; cout << *p1 << endl << *p2 << endl ; }
修改了其中一个,其它的也会改变,因为都是同一个内存地址。然而并不知道这个引用有什么作用,直到将引用作为函数参数的时候才发现挺有用。因为之前必须使用指针才能修改外部的值,现在用引用也可以做到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std ;void num (int &b) { b = 1000 ; } int main (int argc, char *argv[]) { int a = 1 ; num(a); cout << a << endl ; }
引用做为函数返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std ;int & num (int &b) { b = 1000 ; return b; } int main (int argc, char *argv[]) { int a = 1 ; int &c = num(a); cout << c << endl << a << endl ; return 0 ; }
因为当一个函数运行结束之后,所在空间被释放,那么再对其中的局部变量做引用的话,就是非法操作,所以不能对函数的返回引用再次引用(不能引用局部变量)
引用与指针
引用的时候需要初始化,而指针不需要
引用过得变量不能再使用,而指针可以
引用不占用空间,而指针需要占用空间(指针是数据类型,存储地址)
引用效率高于指针,引用直接操作空间,指针通过地址间接操作
引用更加安全,引用是数值操作,指针可能会有地址偏移
引用灵活性没有指针好
&符号的三种用法区分
在声明变量前,表示引用int &a = b;
在变量前面,表示取地址scanf(int, &a);
在数字中间,表示与运算1 & 2;
函数 函数参数默认值 如果只指定部分值,那么后面的参数必须有指定值,而且需要连续,不能间隔指定
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> #include <string> using namespace std ;void num (int age, int score=0 , string name="昊天" ) { cout << "age:" << age << endl << score <<endl << name << endl ; } int main (void ) { num(20 , 99 , "斗罗" ); }
函数重载 同一作用域内,函数名相同,但是参数不同(包括参数数量和参数类型)的互相为重载函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> #include <string> using namespace std ;void num (int age, int score=0 , string name="昊天" ) { cout << "age:" << age << endl << score <<endl << name << endl ; } void num (int age) { cout << "age:" << age << endl ; } int main (void ) { num(20 , 99 , "斗罗" ); }
函数重载可以让函数根据参数类型,自动选择对应的函数。
构造函数 对对象的数据进行初始化,在实例化对象的时候自动调用。
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 ;class Student { private : int age; public : Student() { age = 22 ; } void get_age (void ) { cout << age << endl ; } }; int main (void ) { Student stu1; stu1.get_age(); return 0 ; }
如果构造函数需要接收参数,那么在实例化的时候需要传参
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 #include <iostream> using namespace std ;class Student { private : int age; public : Student(int a = 22 ) { age = a; } void get_age () { cout << age << endl ; } }; int main (void ) { Student stu; stu.get_age(); Student stu1 (18 ) ; stu1.get_age(); Student *stu2 = new Student(33 ); stu2 -> get_age(); return 0 }
初始化列表 对构造函数的数据进行初始化,而非赋值
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 ;class Student { private : int age; public : Student():age(14 ) {} void get_age () { cout << age << endl ; } }; int main (void ) { Student stu; stu.get_age(); return 0 ; }
析构函数 在对象销毁时自动调用,一般用在销毁指针变量时
不能进行重载
没有参数
函数名为~类名
指针对象需要手动释放,所以在调用delete时,才会自动调用析构函数
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 #include <iostream> using namespace std ;class Student { int age; public : Student():age(20 ) { cout << age << endl ; } ~Student() { cout << "end object" << endl ; } }; int main (void ) { { Student stu1; } return 0 ; }
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 #include <iostream> using namespace std ;class Student { int age; public : Student():age(20 ) { cout << age << endl ; } ~Student() { cout << "end object" << endl ; } }; int main (void ) { { Student stu1; } Student *stu2 = new Student; delete stu2; return 0 ; }
临时对象 没有对象名的对象创建方式(必须加括号),其作用域为当前所在语句。语句执行完,对象会被自动销毁。所以临时对象的析构函数会在语句执行完调用
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 ;class Student { int age; public : Student():age(20 ) { cout << age << endl ; } ~Student() { cout << "end object" << endl ; } }; int main (void ) { Student(); return 0 ; }
malloc/free VS new/delete malloc/free无法自动调用构造函数与析构函数
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 ;class Student { int age; public : Student():age(20 ) { cout << age << endl ; } ~Student() { cout << "end object" << endl ; } }; int main (void ) { Student *stu1 = (Student *)malloc (sizeof (Student)); return 0 ; }
this 为系统提供的一个对象指针
虽然this的作用是为了避免同名变量的问题,但是我发现即便是同名的不加this
也没有问题,而老师的视频里面不加this是会出问题的,所以现在的c++已经改变了,可以不用加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 #include <iostream> using namespace std ;class Student { public : int age; Student(int age) { this -> age = age; cout << age << endl ; } ~Student() { cout << "end object" << endl ; } }; int main (void ) { Student stu (22 ) ; return 0 ; }
常函数 在函数后添加const
的函数为常函数,常函数中不可对变量进行修改
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 ;class Student { int age = 20 ; public : void set_age (int age) const { this -> age = age; cout << this -> age << endl ; } }; int main (void ) { Student stu; stu.set_age(22 ); return 0 ; }
常对象 被const修饰的对象为常对象,常对象只能调用常函数,不能调用普通函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std ;class Student { int age = 20 ; public : void set_age (int age) const { cout << this -> age << endl ; } }; int main (void ) { const Student stu; stu.set_age(22 ); return 0 ; }
static 静态成员不能在类内部进行初始化,必须在类外部进行初始化
没有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 #include <iostream> using namespace std ;class Student { static int age; public : void set_age () const { cout << this -> age << endl ; } }; int Student::age = 23 ;int main (void ) { const Student stu; stu.set_age(); return 0 ; }
只有静态常量整形才能在类内部进行初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> using namespace std ;class Student { static const int age = 24 ; }; int main (void ) { Student stu; return 0 ; }
拷贝构造 一个对象以另一个现有对象为基础进行实例化称为拷贝构造
浅拷贝 默认的拷贝构造实现对非静态成员的复制(浅拷贝)
首要条件:拷贝构造函数中传参为局部对象,而且使用引用可以对速度有一定优化
1 2 3 4 5 6 7 8 9 10 Student() { } Student(const Student&variable) { }
拷贝构造的方式:
将现有对象作为参数
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 #include <iostream> using namespace std ;class Student { int age; public : Student() { } Student(const Student&) { age = 18 ; cout << age << endl ; } }; int main (void ) { Student stu1; Student stu2 (stu1) ; Student stu3 = stu1; Student stu4 = Student(stu1); Student *stu5 = new Student(stu1); return 0 ; }
将现有对象作为值
以一个临时对象作为值
1 Student stu4 = Student(stu1);
对象指针中将现有对象作为参数
1 Student *stu5 = new Student(stu1);
深拷贝 浅拷贝存在的问题在于指针变量,当构造函数中有指针变量的时候,而且在析构函数中对指针变量进行了释放,那么在调用拷贝构造后,第一次释放指针变量后,释放拷贝构造的指针时,会因为第一次已经将指针变量释放掉了,而造成程序崩溃。所以如果构造函数中如果存在指针变量,那么就需要用深拷贝,深拷贝则是在构造函数中申请一块新的地址,来存储之前的指针所指向的值
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 #include <iostream> using namespace std ;class Student { public : int *age; Student() { age = new int ; *age = 18 ; } Student(const Student &a) { this ->age = new int ; *age = *(a.age); } ~Student() { delete age; } }; int main (void ) { Student stu; cout << *(stu.age) << endl ; Student stu1 = stu; cout << *(stu1.age) << endl ; return 0 ; }
<cstring>
中的memcpy
可以将连续数组进行复制,适合数组复制
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 #include <iostream> #include <cstring> using namespace std ;class Student { public : int *age; Student() { age = new int [2 ]; age[0 ] = 18 ; age[1 ] = 19 ; } Student(const Student &a) { this ->age = new int [2 ]; memcpy (this ->age, a.age, 8 ); } ~Student() { delete age; } }; int main (void ) { { Student stu; cout << stu.age[0 ] << endl ; Student stu1 = stu; cout << stu1.age[1 ] << endl ; } return 0 ; }
内联函数 在函数名前加inline
,编译时会自动将函数调用的地方替换为函数本体,增加了代码区内存消耗,但是提高了执行速度
优点:避免了常规函数的寻址跳转,节省了cpu寻址时间
缺点:代码量增多,让程序内存占用高一些
类内部的普通函数都是内联函数
内联函数如果写在头文件中,那么需要将定义和函数本体都写在里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std ;inline int max_num (int a, int b) ;int main (void ) { int max = max_num(8 , 4 ); cout << max << endl ; } inline int max_num (int a, int b) { int max = (a > b)?a:b; return max ; }
类 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> #include <string> using namespace std ;class Student { public : string name; int age; string get_name (void ) { return name; } }; int main (void ) { Student stu1; stu1.name = "昊天" ; stu1.age = 20 ; cout << stu1.age << endl ; stu1.get_name(); }
不知道为什么,一直使用不了字符数组char?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> #include <string> using namespace std ;class Student { public : string name; int age; string get_name (void ) { return name; } }; int main (void ) { Student *stu1 = new Student; stu1 -> name = "昊天" ; stu1 -> age = 20 ; cout << stu1 -> age << endl ; }
访问修饰符
public:类外部可以访问(结构体默认为public)
private:仅类内部可以访问,外部不可见
protected:仅类内部以及子类可以访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> #include <string> using namespace std ;class Student { string name = "li" ; int age; public : string get_name (void ) { return name; } }; int main (void ) { Student *stu1 = new Student; string na = stu1 -> get_name(); cout << na << endl ; }
friend 有元:让被private和protected修饰的变量/函数对指定函数/类可见(修饰friend函数/类)
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 #include <iostream> using namespace std ;class Student { private : int age; int get_age (void ) { return age; } friend void fun () ; }; void fun (void ) { Student stu1; stu1.age = 22 ; stu1.get_age(); } int main (void ) { fun(); }
主函数也可被声明为friend函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std ;class Student { private : int age; int get_age (void ) { return age; } friend int main () ; }; int main (void ) { Student stu1; stu1.age = 22 ; stu1.get_age(); return 0 ; }
同样的,类也可以被friend修饰,成为friend类,作用同上
运算符重载 一般来说函数是不可以和数字相加的,因为数据类型不一致,但是可以使用运算符重载来达到这个目的,它的主要意义还是对于同一个类的多个对象
类外部运算符重载 1 2 3 4 operator +(class_name_reference, variable){ }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std ;class Num { public : int a = 10 ; }; void operator +(Num &v, int b){ cout << v.a + b << endl ; } int main (void ) { Num num1; num1 + 10 ; return 0 ; }
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 #include <iostream> using namespace std ;class Num { public : int a = 10 ; }; void operator +(Num &v, int b){ cout << v.a + b << endl ; } void operator +(int b, Num &v){ cout << v.a + b << endl ; } int main (void ) { Num num1; num1 + 10 ; 20 + num1; return 0 ; }
类内部运算符重载 参数不需要写类,默认左边为类
1 2 3 4 operator +(variable){ }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std ;class Num { public : int a = 10 ; int operator +(int b) { cout << a + b << endl ; return a+b; } }; int main (void ) { Num num1; num1 + 10 ; return 0 ; }
因为默认左边为类的参数,所以不能实现左边为数字,右边为类的这种运算
i/ostream重载 cout是ostream的一个对象
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 #include <iostream> using namespace std ;class Out { int age; public : Out() { age = 18 ; } friend ostream & operator <<(ostream &os, const Out &out); }; ostream & operator <<(ostream &os, const Out &out) { os << out.age ; return os; } int main (void ) { Out ot1; cout << ot1 << " year's old" << endl ; return 0 ; }
cin是istream的一个对象
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 #include <iostream> using namespace std ;class In { private : int age; friend istream & operator >>(istream &is, In &in); public : void get_age () { cout << this ->age << endl ; } }; istream & operator >>(istream &is, In &in) { is >> in.age; if (is.fail()) { in.age = 0 ; cout << "input error" << endl ; } return is; } int main (void ) { In in1; cin >> in1; in1.get_age(); return 0 ; }
继承 将公共的类作为基类,被子类继承后,子类也会拥有基类的所有属性(成员变量,成员函数)
1 2 3 4 class class_name : permission basic_class_name{ }
权限修饰符:修饰子类的继承之后的权限
public:拥有与父类一致的权限
protected:父类的public降低为protected,其它不变
private:所有成员变为private
如果不写权限修饰符,那么默认为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 #include <iostream> using namespace std ;class School { int school_num; public : School() { school_num = 1 ; cout << school_num << endl ; } }; class Student : public School { int stu_num; public : Student() { stu_num = 1001 ; cout << stu_num << endl ; } }; int main (void ) { Student stu1; return 0 ; }
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 #include <iostream> using namespace std ;class School { int school_num; public : School(int a) { school_num = a; cout << school_num << endl ; } }; class Student : public School{ int stu_num; public : Student() : School(1 ) { stu_num = 1001 ; cout << stu_num << endl ; } }; int main (void ) { Student stu1; return 0 ; }
析构函数继承 先执行子类的析构,再执行父类的析构,一级一级的向上析构
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 #include <iostream> using namespace std ;class School { int school_num; public : School(int a) { school_num = a; cout << school_num << endl ; } ~School() { cout << "father 析构" << endl ; } }; class Student : public School{ int stu_num; public : Student() : School(1 ) { stu_num = 1001 ; cout << stu_num << endl ; } ~Student() { cout << "son 析构" << endl ; } }; int main (void ) { Student stu1; return 0 ; } 1 1001 son 析构 father 析构
同名成员的覆盖 如果子类中有与父类中同名的成员(数据成员,函数成员),那么子类中的成员会对父类的成员进行覆盖(但是也可以使用类名作用域来调用父类成员)
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 #include <iostream> using namespace std ;class School { public : int s_num = 10 ; void get_num () { cout << s_num << endl ; } }; class Student : public School{ public : int s_num = 20 ; void get_num () { cout << s_num << endl ; cout << School::s_num << endl ; } }; int main (void ) { Student stu1; stu1.get_num(); stu1.School::get_num(); return 0 ; } 20 10 10
friend不可继承 父类的friend函数不可被子类继承
static公共 static成员是公共的,都可以使用,但不属于继承的范围
多态 多态:父类的指针,可以有多种执行状态,则被称为多态。
多态是一种范型编程思想,虚函数是实现范型编程思想的基础
一般而言,父类指针只能指向父类的成员
1 School *sl = new School;
但是c++中,父类指针也可以指向子类成员
1 School *sl = new Student;
不过不能访问子类的成员
虚函数
前提条件:
特点:
父类中为虚函数,子类默认也会为虚函数,但是省略了virtual。如果子类被继承,那么子类调用其子类也会是调用其子类的函数
不能是inline函数
构造函数不能是虚函数
虚函数可以让父类的指针不仅指向子类,还能使用子类的函数(为此,实现了多态)。只需要在父类的函数名前加virtual
其原理是:当对象以指针类型创建时,而且存在虚函数时,那么会创建一个虚表。虚表的首地址存在对象指针的前8字节,虚表数组中存储的是虚函数的地址。并且子类中复写的虚函数的地址会替换掉父类虚表中虚函数的地址
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> using namespace std ;class School { public : int s_num = 10 ; virtual void get_num () { cout << s_num << endl ; } }; class Student : public School{ public : int s_num = 20 ; void get_num () { cout << s_num << endl ; cout << School::s_num << endl ; } }; int main (void ) { School *sl = new Student; sl->get_num(); delete sl; return 0 ; }
虚表
对象创建时,如果存在虚函数,哪么会创建一个虚表来存放虚函数地址。对象的首地址(前八字节)存放的值为虚函数的首地址。取到前八字节的内容即取到了虚表的地址
虚表中存放的都是虚函数的地址,由于每个函数的返回值不同,所以存储的内容大小不同,需要取前八字节来获得函数地址
函数调用的实质是指针的调用,使用pointer()
即可调用函数
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 #include <iostream> using namespace std ;class School { public : virtual void he () { cout << "hello" << endl ; } int s_num = 10 ; virtual void get_num () { cout << "s_num" << endl ; } }; class Student : public School{ public : virtual void he () { cout << "hello" << endl ; } int s_num = 20 ; void get_num () { cout << "s_num" << endl ; } }; int main (void ) { School *sh = new Student; typedef void (*p) () ; cout << sizeof (sh)<<endl ; cout << *(long *)sh << endl ; cout << ((long *)*(long *)sh + 0 ) << endl ; ((p)(*((long *)*(long *)sh+0 )))(); ((p)(*((long *)*(long *)sh+1 )))(); int si = (int )(*((long *)*(long *)sh+1 )); cout << si << endl ; delete sh; return 0 ; } 8 94101996871008 0x5595d4189d60 hello s_num -736595012
虚析构 如果不让析构函数成为虚函数,那么父类指针指向子类时,释放父类指针时,只会调用父类的析构函数,不会调用子类的析构函数。所以需要在父类的析构函数成为虚析构,在析构函数前面加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 #include <iostream> using namespace std ;class School { public : virtual void he () { cout << "hello" << endl ; } virtual ~School() { cout << "父类析构" << endl ; } }; class Student : public School{ public : virtual void he () { cout << "hello" << endl ; } virtual ~Student() { cout << "子类析构" << endl ; } }; int main (void ) { School *sh = new Student; delete sh; return 0 ; } 子类析构 父类析构
纯虚函数 在父类中只声明了函数,没有函数主体的虚函数称为纯虚函数。
1 virtual void function_name () = 0 ;
有纯虚函数的类,必须在子类中重写这个纯虚函数,才能实例化对象(子类)
有纯虚函数的类,称为抽象类(不能实例化对象)
全部为纯虚函数的类,称为接口类(可以包含成员变量,虚析构,构造函数)
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 ;class School { public : virtual void say () = 0 ; }; class Student : public School{ public : virtual void say () { cout << "hello" << endl ; } }; int main (void ) { Student stu; return 0 ; }
虚继承 在多继承中存在一种情况:A为父类,B,C分别继承与A,D继承了B和C,那么D访问A中的成员时,就会出现访问不明确的问题。因为B,C继承时都是赋值了一份A中的成员,D访问时,不知道是访问B中的,还是C中的。所以需要虚继承来解决这个问题
1 2 3 4 5 class A () {};class B () :virtual public A {};class C () :virtual public C {};class D():public B, public C{};
虚继承存在的问题:结构复杂,内存消耗大。
单例模式 一个类只能有一个实例化对象,如果要实例化其他的对象,需要将之前的对象删除掉。为了实现这种设计模式,需要满足的条件:
构造函数是private或者protected(这样就不能在类外直接实例化对象)
类内部使用static函数实现对类的实例化(使用static函数返回一个对象指针)
使用一个static变量作为标记进行判断(达到只实例化一次的目的)
析构函数中改变static变量标记值(实现删除对象后可以再次实例化)
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 #include <iostream> using namespace std ;class St { private : St() { cout << "实例化对象" << endl ; } public : static int flag; static St* init () { if (flag == 0 ) { flag = 1 ; return new St; } else { return NULL ; } } ~St() { flag = 0 ; } }; int St::flag = 0 ; int main (void ) { St *st1 = St::init(); delete st1; St *st2 = St::init(); delete st2; return 0 ; }
异常 1 2 3 4 5 6 7 8 9 try { } throw a;catch (a) { }
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 #include <iostream> using namespace std ;int main (void ) { double a ; double b ; cin >> a >> b; try { cout << a << " " << b << endl ; if (b == 0 ) { char e[] = "error !" ; throw e; } else { double c = a / b; cout << a << "/" << b << "= " << c << endl ; } } catch (char e[]) { cout << e << " second number cannot be 0" << endl ; } catch (...) { cout << "second number cannot be 0" << endl ; } return 0 ; }
内部类 即一个类中嵌套另外一个类,这种情况比较麻烦
外部类访问内部类成员:需要在外部类中实例化一个内部类的对象。通过对象去访问
内部类访问外部类成员:需要在外部类实例化外部类,并且将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 #include <iostream> using namespace std ;class Ot { public : int a = 10 ; class Is { public : Ot *outside; int a; Is(Ot *o):outside(o) { a = outside->a; } void show () { cout << outside->a << endl ; } }; public : Is inside; Ot():inside(this ){}; }; int main (void ) { Ot outside; outside.a = 30 ; cout << "内部类 a= " << outside.inside.a << endl ; outside.inside.show(); return 0 ; }
函数模板 通过函数模板可以实现之前函数重载的功能,对传入参数进行智能识别
1 2 template <typename customer_type_name>return type fun_name(customer_type_name arg){}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std ;template <typename T> void num (T n) { cout << n+1 << endl ; } int main (void ) { num(3.3 ); return 0 ; }
函数模板的具体化 如果给函数传入一个结构体,而非普通的数据类型,那么可以对函数模板进行具体化,让结构体走不同的模板
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 #include <iostream> using namespace std ;template <typename T>void num (T n) { cout << n << endl ; } struct list { int a; int b; }; template <> void num<list >(list li){ cout << li.a << " " << li.b << endl ; } int main (void ) { num("hello" ); list lt = {10 , 20 }; num(lt); return 0 ; }
类模板 如果类在实例化的时候构造函数需要传入参数,而这个参数类型不确定,那么可以使用类模板来达到自动识别参数类型的作用(实例化对象的时候手动指定类型)
1 2 3 4 5 6 7 8 9 template <typename customer_type_name>class class_name { customer_type_name variable; class_name(customer_type_name arg) { variable = arg; } }
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 #include <iostream> using namespace std ;template <typename T = int > class Student { T num; public : Student(T n){ num = n; } void get_n () { cout << num << endl ; } }; int main (void ) { Student<> stu1 (22 ) ; stu1.get_n(); Student<char *> stu2 ((char *)"no.1" ) ; stu2.get_n(); Student<double > *p = new Student<double >(12.12 ); p->get_n(); delete p; return 0 ; }
类外函数模板 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 #include <iostream> using namespace std ;template <typename T = int >class Student { T num; public : Student(T n){ num = n; } void get_n () { cout << num << endl ; } void set_n (T n) ; }; template <typename T> void Student<T>::set_n(T n) { num = n; } int main (void ) { Student<> stu1 (22 ) ; stu1.get_n(); Student<char *> stu2 ((char *)"no.1" ) ; stu2.get_n(); Student<double > *p = new Student<double >(12.12 ); p->set_n(22.22 ); p->get_n(); return 0 ; } 22 no.1 22.22
继承类模板的使用 如果子类继承了一个有类模板的父类,那么在子类中需给定模板类型:可以给定指定的类型,也可以让子类拥有类模板,动态给定类型
指定固定类型 指定后父类的模板类型固定
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 #include <iostream> using namespace std ;template <typename T = int >class Student { T num; public : Student(T n){ num = n; } void get_n () { cout << num << endl ; } void set_n (T n) ; }; template <typename T>void Student<T>::set_n(T n){ num = n; } class User : public Student<int > { public : User() : Student(100 ) { } }; int main (void ) { User user1; user1.set_n(111.111 ); user1.get_n(); return 0 ; }
动态指定类型 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 ;template <typename T = int >class Student { T num; public : Student(T n){ num = n; } void get_n () { cout << num << endl ; } void set_n (T n) ; }; template <typename T>void Student<T>::set_n(T n){ num = n; } template <typename U> class User : public Student<U>{ public : User() : Student<U>(100 ) { } }; int main (void ) { User<double > user1; user1.set_n(111.111 ); user1.get_n(); return 0 ; }
多态模板 类似上面动态指定类型
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 #include <iostream> using namespace std ;template <typename T = int >class Student { T num; public : Student(T n) { num = n; } virtual void get_n () { cout << num << endl ; } }; template <typename U>class User : public Student<U>{ public : User():Student<U>(100 ){}; virtual void get_n () { cout << "虚函数" << endl ; } }; int main (void ) { Student<double > *p = new User<double >; p->get_n(); delete p; return 0 ; } 虚函数