C++学习笔记三(类和对象)
1)访问权限:
2)struct和class的区别
3)成员属性私有化
1)构造函数和析构函数
2)构造函数的分类和调用
3)构造函数的调用规则
4)深拷贝与浅拷贝
5)初始化列表
6)静态成员
7)this指针的用途
8)空指针访问成员函数
9)const修饰成员函数(常函数)
10)友元
1)重载加号+
2)重载左移<<
3)重载++运算符
4)重载赋值运算符=
5)重载关系运算符==
6)重载函数调用运算符()----仿函数
1)继承语法
2)继承方式
3)构造函数和析构函数的执行顺序
4)继承同名成员处理方式
5)继承同名静态成员处理方式
6)多继承
7)菱形继承与虚继承
1)语法
2)动态多态代码实例
3)纯虚函数和抽象类
4)虚析构和纯虚析构
class Circle { public://访问权限 double r; double GetArea() { return r * r * 3.14; } }; int main() { Circle c1; c1.r = 10; cout << c1.GetArea() << endl; system("pause"); return 0; }
1)访问权限:
①public公共权限 成员类内可以访问,类外也可以访问。
②protected保护权限 成员类内可以访问,类外不可以访问,但是儿子可以访问。
③private私有权限 成员类内可以访问,类外不可以访问,儿子也不可以访问。
2)struct和class的区别
struct成员默认权限是公有public,class成员默认权限是私有private。
3)成员属性私有化
class Student { private: int age; string name; string num; public: int GetAge() { return this->age; } void SetAge(int age) { this->age = age; } }; int main() { Student stu1; stu1.SetAge(21); cout << stu1.GetAge(); system("pause"); return 0; }
1)构造函数和析构函数
①构造函数语法:类名(){};
②析构函数语法:~类名(){};
2)构造函数的分类和调用
分类:
①有参构造和无参构造
②拷贝构造函数:
Student(const Student& stu) { //....... }
调用:
①调用默认参数时不需要加(),不然,Student stu()会被认为是一个函数声明:
Student stu;
②调用有参构造:
Student stu1=Student(10);
③调用拷贝构造:
Student stu2=Student(stu1);
④匿名对象:
Student(10);//当前行执行结束,系统会立即回收掉匿名对象
⑤隐式转换法:
Student stu1=10;//相当于Student stu1=Student(10) Student stu2=stu1;//相当于拷贝构造
3)构造函数的调用规则:
①创建一个类,C++编译器会给每个类添加至少三个函数:默认构造,析构函数,拷贝构造。
②如果用户定义有参构造函数,那么编译器将不再提供无参构造函数,但是会提供默认拷贝构造函数。
③如果用户定义拷贝构造函数,那么C++将不会提供其他构造函数。
4)深拷贝与浅拷贝
浅拷贝:
Student(const Student &stu) { age=stu.age; }
深拷贝(重新再堆区创建一块内存保存数据):
Student(const Student &stu) { age=new int(*stu.age); }
总结:如果有属性是在堆区开辟的内存,就要自己构造拷贝函数,防止浅拷贝带来的问题。
5)初始化列表
Student(int a, string b, string c) :age(a),name(b),num(c) { }
等价于:
Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; }
6)静态成员
①定义:在成员前面加上static称为静态成员。
②调用静态成员(两种方法):
通过对象访问:
Person p;
p.func();
通过类名访问:
Person::func();
③静态成员函数可以访问静态成员变量,但是不可以访问非静态成员变量,因为无法区分到底是哪个对象的变量。
7)this指针的用途
①当形参和成员变量同名时,可以用this来区分;
②在类的非静态成员函数中返回对象本身,可以使用return *this。
8)空指针访问成员函数
class Student { private: int age; string name; string num; public: Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; } void ShowAge() { if (this == NULL) return; cout << age << endl; } void ShowName() { cout << "hello world" << endl; } }; int main() { Student* s = NULL; s->ShowAge(); system("pause"); return 0; }
9)const修饰成员函数(常函数)
①常函数内不可以修改成员属性;
成员属性声明时加关键字mutable后,就可以修改成员属性了。
class Student { private: int age; mutable string name; string num; public: Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; } void ShowName() const { this->name = "jack"; cout << "hello world" << endl; } };
②常对象只能调用常函数
class Student { private: int age; mutable string name; string num; public: Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; } void ShowAge() { if (this == NULL) return; cout << age << endl; } void ShowName() const { this->name = "jack"; cout << "hello world" << endl; } }; int main() { const Student s; s.ShowName(); system("pause"); return 0; }
10)友元
①友元的目的是为了让一个函数或者类访问另一个类中的私有成员。
友元关键字为friend。
②友元的三种实现:
全局函数做友元
class Student { //全局函数可以访问当前类对象的私有成员 friend void ShowInfor(Student s); private: int age; mutable string name; string num; public: Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; } }; //全局函数 void ShowInfor(Student s) { cout << s.age << endl; }
类做友元
class Student { //另一个类可以访问当前类对象的私有成员 friend class Teacher; private: int age; mutable string name; string num; public: Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; } }; class Teacher { private: string name; int age; public: void Visit(Student s) { cout << s.name << s.age << endl; } };
成员函数做友元
class Student { //Visit1可以访问当前类对象的私有成员,Visit2不能 friend void Teacher::Visit1(Student s); private: int age; mutable string name; string num; public: Student(int age,string name,string num) { this->age = age; this->name = name; this->num = num; } }; class Teacher { private: string name; int age; public: void Visit1(Student s) { cout << s.name << s.age << endl; } void Visit2(Student s) { } };
1)加号运算符重载
①通过成员函数重载加号
class Person { public: int age; Person(int age) { this->age = age; } //通过成员函数 Person operator+(Person &p) { Person p1 = Person(30); p1.age = this->age + p.age; return p1; } };
②通过全局函数重载加号
class Person { public: int age; Person(int age) { this->age = age; } }; //通过全局函数 Person operator+(Person& p1, Person& p2) { Person p = Person(10); p.age = p1.age + p2.age; return p; } int main() { Person p1 = Person(10); Person p2 = Person(20); Person p3 = p1 + p2; cout << p3.age << endl; system("pause"); return 0; }
2)左移运算符
只能通过全局函数重载左移运算符
class Person { public: string name; int age; Person(string a,int b):name(a),age(b) { } }; ostream &operator<<(ostream &cout, Person &p) { cout << p.name << endl << p.age; return cout; } int main() { Person p = Person("jack", 19); cout << p << endl; system("pause"); return 0; }
3)重载++运算符
class Person { friend ostream &operator<<(ostream& cout, Person& p); private: string name; int age; public: Person(string a,int b):name(a),age(b) { } //前置++运算符 Person& operator++() { this->age++; return *this; } //后置++运算符,int用于区分前置运算符 Person operator++(int) { Person temp = *this; age++; return temp; } }; ostream &operator<<(ostream &cout, Person &p) { cout << p.name << endl << p.age; return cout; } int main() { Person p = Person("jack", 19); cout << ++p << endl; system("pause"); return 0; }
4)赋值运算符
class Person { friend ostream &operator<<(ostream& cout, Person& p); private: public: int* age; Person(int b) { age = new int(b); } ~Person() { if (age != NULL) { delete age; age = NULL; } } Person& operator==(Person p) { //先判断是否有属性在堆区,若有先释放干净,然后再深拷贝 if (age != NULL) { delete age; age = NULL; } //深拷贝 age = new int(*p.age); return *this; } }; ostream &operator<<(ostream &cout, Person &p) { cout << p.age<<endl; return cout; } int main() { Person p1 = Person(19); Person p2 = Person(20); p2 = p1; cout << *p2.age << endl; system("pause"); return 0; }
5)关系运算符
class Person { public: int age; Person(int b):age(b) { } bool operator==(Person p) { if (p.age == age) return true; else return false; } }; int main() { Person p1 = Person(19); Person p2 = Person(20); bool f = p1 == p2; cout << f; system("pause"); return 0; }
6)重载函数调用运算符(),也称为仿函数
class Person { public: int age; Person(int b):age(b) { } void operator()(string s) { cout << s << endl; } }; int main() { Person p1 = Person(19); Person p2 = Person(20); p1("shidhapi"); system("pause"); return 0; }
1) 继承语法:class 子类: 继承方式 父类
2)继承方式(3种):①公共继承;②保护继承;③私有继承。
3)构造函数和析构函数的执行顺序
父类构造函数->子类构造函数->子类析构函数->父类析构函数
4)继承同名成员处理方式
①同名成员属性处理方式
#include <iostream> using namespace std; class Base { public: Base() { m_A = 100; } int m_A; }; class Son : public Base { public: Son() { m_A = 200; } int m_A; }; void test01() { Son s; cout << "Son 下 m_A = " << s.m_A << endl; //如果通过子类对象 访问到父类中的同名成员 需要加作用域 cout << "Base 下 m_A = " << s.Base::m_A << endl; } int main() { test01(); return 0; }
②同名成员函数处理方式
#include <iostream> using namespace std; class Base { public: Base() { m_A = 100; } void func() { cout << "Base fun()调用" << endl; } void func(int a) { cout << "Base fun(int a)调用" << endl; } int m_A; }; class Son : public Base { public: Son() { m_A = 200; } void func() { cout << "Son fun()调用" << endl; } int m_A; }; void test01() { Son s; s.func();//直接调用 调用是子类中的同名成员 //如何调用父类中同名成员函数? s.Base::func(); s.Base::func(100); } int main() { test01(); return 0; }
5)继承同名静态成员处理方式
①同名静态成员属性
#include <iostream> using namespace std; //继承中的同名静态成员处理方式 class Base { public: static int m_A; }; int Base::m_A = 100; class Son: public Base { public: static int m_A; }; int Son::m_A = 200; //同名静态成员属性 void test01() { Son s; //1.通过对象访问 cout << "Son 下 m_A = " << s.m_A << endl; cout << "Base 下 m_A = " << s.Base::m_A << endl; //2.通过类名访问 cout << "Son 下 m_A = " << Son::m_A << endl; cout << "Base 下 m_A = " << Base::m_A << endl; //第一个::代表通过类名方式访问,第二个::代表访问父类作用域下 cout << "通过子类访问父类m_A = " << Son::Base::m_A << endl; } int main() { test01(); return 0; }
②同名静态成员函数
#include <iostream> using namespace std; //继承中的同名静态成员处理方式 class Base { public: static int m_A; static void func() { cout << "Base static void func()" << endl; } static void func(int a) { cout << "Base static void func(int a)" << endl; } }; int Base::m_A = 100; class Son: public Base { public: static void func() { cout << "Son static void func()" << endl; } static int m_A; }; int Son::m_A = 200; //同名静态成员属性 void test01() { Son s; //1.通过对象访问 s.func(); s.Base::func(); //2.通过类外访问 Son::func(); Base::func(); Son::Base::func(); Son::Base::func(100); } int main() { test01(); return 0; }
6)多继承语法
class 子类: 继承方式 父类1,继承方式 父类2,。。。
多继承可能会引发父类中有同名成员出现,需要加作用域区分。实际开发中不建议用多继承
7)菱形继承与虚继承
①概念:2个派生类继承自同一个基类,又有同一个类继承这2个派生类,这种继承被称为菱形继承,或钻石继承。
②菱形继承的问题:这种继承方式也存在数据的二义性,这里的二义性是由于他们间接都有相同的基类导致的。 这种菱形继承除了带来二义性之外,还会浪费内存空间。
③解决方法:虚继承
class A { public : int num; A() { num = 10; } }; class B :virtual public A { public: B() { num = 20; } }; class C :virtual public A { public: C() { num = 30; } }; class D :public B, public C { public: D() { num = 50; } }; int main() { D d; cout << d.num << endl; system("pause"); return 0; }
1)分类(2类)
①静态多态:函数重载以及运算符重载属于静态重载,复用函数名。
②动态多态:派生类和虚函数实现运行时多态。
2)动态多态代码实例
动态多态满足条件:①有继承关系;②子类重写父类虚函数。
class Father { public: void Face() { cout << "Father's face" << endl; } virtual void Say() { cout << "Father say hello" << endl; } }; class Son:public Father { public: void Say() { cout << "Son say hello" << endl; } }; void main() { Son son; Father *pFather=&son; // 隐式类型转换 pFather->Say(); }
3)纯虚函数和抽象类
①纯虚函数语法:virtual 返回类型 函数名(参数列表)=0
②抽象类概念:只要有一个纯虚函数,这个类就称为抽象类。
③抽象类特点:1)无法实例化对象;2)抽象类的子类必须要重写父类中的纯虚函数,否则也属于抽象类。
④代码实例:
class A { public : int num; //纯虚函数 virtual void func() = 0; }; class B :public A { public: virtual void func(){} };
4)虚析构和纯虚析构
虚析构和纯虚析构可以解决父类指针释放子类对象时不干净的问题。
纯虚析构需要声明也需要实现。