C++类和对象(上):https://developer.aliyun.com/article/1459438
const 静态成员
对于一个类的成员,既要实现共享,又要实现不可变,那就用const修饰。定义静态const成员时,最好在类内部初始化。
this指针
C++封装性质:将数据和方法封装在一起
数据和方法是分开存储的。每个对象拥有独立的数据,但是对象方法是共享的。每个方法调用的时候都会隐式存在一个this指针。
C++规定:
- this指针是隐含在成员函数内的一种指针,当一个对象被创建后,他的每一个成员函数都含有一个系统自动生成的隐含指针this,用来保存这个对象的地址。
- 成员函数通过this指针即可知道操作的数据对象是谁
- 隐藏在每一个非静态成员函数中,静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量
使用
- 当形参和成员变量同名的时候可以使用this来区分。
- 在类的普通成员函数中返回对象本身,可以使用 return *this;
const
const 修饰成员函数
使用const修饰成员函数时候,const修饰this指针指向的区域,成员函数体内不可以修改奔类中对的任何普通成员变量,放成员变量类型钱用mutable修饰时候例外。
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { public: int getA() const { a = 50; return a; } private: mutable int a{0}; }; int main(int argc, char* argv[]) { Person p; cout << p.getA() << endl; return 0; }
函数如果不会修改成员函数的数据,就给函数加const。表示在此函数内不会给成员函数赋值。
const 修饰对象
const修饰对象叫常对象,编译器认为普通函数都有修改成员变量的可能。
因此不能调用非const修饰的函数。
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { public: void setA(int a) const { // this->a = a; } int getA() const { a = 50; return a; } private: mutable int a{0}; }; int main(int argc, char* argv[]) { const Person p; cout << p.getA() << endl; return 0; }
友元
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部访问。但是有时候需要在类的外部访问类的私有成员,怎么办?解决方法就是使用友元函数,友元函数是一种特权函数,C++允许这个特权函数访问私有成员。程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。
普通全局函数作为友元
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { friend int getA(const Person &p); public: private: int a{0}; }; int getA(const Person &p) { return p.a; } int main(int argc, char* argv[]) { const Person p; cout << getA(p) << endl; return 0; }
类的成员函数作为另一个类的友元
#include <iostream> #include <string> #include <iostream> using namespace std; class Person; class Dog { public: int getA(const Person &p); }; class Person { friend int Dog::getA(const Person &p); public: private: int a{0}; }; int Dog::getA(const Person &p) { return p.a; } int main(int argc, char* argv[]) { const Person p; Dog d; cout << d.getA(p) << endl; return 0; }
类作为另一个类的友元
#include <iostream> #include <string> #include <iostream> using namespace std; class Person; class Dog { public:s int getA(const Person &p); }; class Person { friend class Dog; public: private: int a{0}; }; int Dog::getA(const Person &p) { return p.a; } int main(int argc, char* argv[]) { const Person p; Dog d; cout << d.getA(p) << endl; return 0; }
运算符的重载
运算符重载就是对已有的运算符重新进行定义,赋予其另外一种功能,以使用不同的数据类型。
运算符重载只是一种语法上的方便,也就是它只是另一种函数调用。
语法:函数名是由关键字operator紧跟着运算符
比如重载+ : operator+
注意:重载运算符不要更改运算符的本质。
可重载的运算符
几乎C中的所有运算符都可以重载,但是运算符重载的使用是相当受限制的。特别是不能使用C中当前没有意义的运算符,不能改变运算符的优先级,不能改变运算符的参数个数,这样的限制有意义,否则所有的这些行为产生的运算符只会混淆,而不是澄清寓意。
+ - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* ' -> [] () new delete new[] delete[]
不能重载的运算符有: . :: .* ?: sizeof
运算符与友元函数
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { friend ostream &operator<<(ostream &out, const Person &p); friend Person operator+(const Person p1, const Person &p2); public: private: int a{5}; }; ostream &operator<<(ostream &out, const Person &p) { return out << p.a; } Person operator+(const Person p1, const Person &p2) { Person p; p.a = p1.a + p2.a; return p; } int main(int argc, char* argv[]) { Person p; Person p2; cout << p << endl; cout << p2 + p << endl; return 0; }
运算符与成员函数
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { friend ostream &operator<<(ostream &out, const Person &p); public: Person operator+(const Person &p2) { Person p; p.a = this->a + p2.a; return p; } private: int a{5}; }; ostream &operator<<(ostream &out, const Person &p) { return out << p.a; } int main(int argc, char* argv[]) { Person p; Person p2; cout << p << endl; cout << p2 + p << endl; return 0; }
重载++/–
重载++和–运算符 需要区分是前置++/–还是后置++/–;区分方式是按照参数个数区分。
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { friend ostream &operator<<(ostream &out, const Person &p); public: Person operator+(const Person &p2) { Person p; p.a = this->a + p2.a; return p; } Person operator++(int) { Person p = *this; this->a++; return p; } Person &operator++() { ++this->a; return *this; } Person operator--(int) { Person p = *this; this->a--; return p; } Person &operator--() { --this->a; return *this; } private: int a{5}; }; ostream &operator<<(ostream &out, const Person &p) { return out << p.a; } int main(int argc, char* argv[]) { Person p; Person p2; cout << p << endl; cout << p2 + p << endl; cout << p++ << endl; cout << p << endl; cout << ++p2 << endl; cout << p-- << endl; cout << p << endl; cout << --p2 << endl; return 0; }
重载->和*
#include <iostream> #include <string> #include <iostream> using namespace std; class Person { friend ostream &operator<<(ostream &out, const Person &p); public: Person() { cout << "Person" << endl; } ~Person() { cout << "~Person" << endl; } void show() { cout << "aaa" << endl; } private: int a{5}; }; class SmartPoint { public: SmartPoint(Person *p) { this->p = p; } ~SmartPoint() { delete p; } Person* operator->() { return this->p; } Person& operator*() { return *this->p; } private: Person *p; }; ostream &operator<<(ostream &out, const Person &p) { return out << p.a; } int main(int argc, char* argv[]) { SmartPoint point(new Person); point->show(); (*point).show(); return 0; }
注意: 类中无指针成员的时候,无需重写=运算符,斗则必须重载=运算符
重载赋值运算符
指针作为类中的成员:
- 拷贝构造函数必须自定义(默认拷贝构造是浅拷贝)
- 必须重载=运算符(默认=运算符是浅拷贝)
如果不重写拷贝并且也不重载=运算符,编译器会给自动创建这些函数,默认的行为类似于浅拷贝。
#include <iostream> #include <string> #include <iostream> #include <algorithm> using namespace std; class Person { friend ostream &operator<<(ostream &out, const Person &p); public: Person() { cout << "Person" << endl; } ~Person() { cout << "~Person" << endl; } private: int a{5}; }; class SmartPoint { public: SmartPoint() { } SmartPoint(Person *p) { this->p = p; } ~SmartPoint() { delete p; } explicit SmartPoint(const SmartPoint &other) { p = new Person(*other.p); } SmartPoint& operator=(const SmartPoint &other) { cout << "赋值操作" << endl; p = new Person(*other.p); return *this; } Person *p; }; ostream &operator<<(ostream &out, const Person &p) { return out << p.a; } int main(int argc, char* argv[]) { SmartPoint point(new Person); SmartPoint point2(point); SmartPoint point3; point3 = point; cout << *(point.p) << endl; cout << *(point2.p) << endl; cout << *(point3.p) << endl; return 0; }
重载 != 、 == 、 () 等运算符号
#include <iostream> #include <string> #include <iostream> #include <algorithm> using namespace std; class Person { friend ostream &operator<<(ostream &out, const Person &p); public: Person(int a) : a(a) {} bool operator==(const Person &other) { return this->a == other.a; } bool operator!=(const Person &other) { return this->a != other.a; } Person & operator()(int a) { this->a += a; return *this; } private: int a{5}; }; ostream &operator<<(ostream &out, const Person &p) { return out << p.a; } int main(int argc, char* argv[]) { Person p(5); Person p2(6); Person p3(5); cout << (p==p2) << endl; cout << (p!=p3) << endl; Person p6(2); cout << p6(5) << endl; cout << Person(9)(5) << endl; return 0; }
注意: 不要重载 && || ,因为用户无法实现 && 和 || 具有短路特性
总结: =,[],(),->操作符号只能通过成员函数进行重载,<< 和 >> 只能通过全局函数配合友元函数进行重载,不要重载&&和||操作符号
运算符 | 使用建议 |
所有一元运算符 | 成员 |
= () [] -> ->* | 必须是成员 |
+= -= /= *= ^= &= != %= >>= <<= | 成员 |
其他二元运算符 | 非成员 |
继承和派生
C++最终要的特性是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类型不仅拥有旧类的成员,还拥有新定义的成员。一个B类继承自A类或者称为从A类派生出B类。这样的话A类称为基类(父类),B类称为派生类(子类),派生类中的成员包含两大部分:一类是从基类继承过来的,一类是自己增加的成员,从基类继承过来的表现其共性,而新增的成员体现了其个性。
子类继承于父类
父类派生出子类
继承的访问控制
代码示例:
class Derive : 继承方式 Base [, 继承方式 Base2 ... ] { // ...... }
继承方式分类:
访问权限:
- public:公有继承
- protected:保护继承
- private:私有继承
父类个数:
单继承:指每个派生类只继承了一个基类的特征。
多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特性。
注意: 子类继承父类,子类拥有父类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在派生类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限。
代码示例
public继承:在类外可以访问父类的public变量,在类内可以访问public和protected数据。私有数据在子类内外都是不可见的。
#include <iostream> #include <string> #include <iostream> #include <algorithm> using namespace std; class Base { public: int a{1}; protected: int b{2}; private: int c{3}; }; class Derive : public Base { public: void show() { cout << a << b << endl; } }; int main(int argc, char* argv[]) { Derive d; cout << d.a << endl; return 0; }
protected继承:在类内可以访问public和protected数据。类外无法访问;private是不可见数据。
#include <iostream> #include <string> #include <iostream> #include <algorithm> using namespace std; class Base { public: int a{1}; protected: int b{2}; private: int c{3}; }; class Derive : protected Base { public: void show() { cout << a << b << endl; } }; int main(int argc, char* argv[]) { Derive d; return 0; }
private继承:在类内可以访问public和protected数据。类外无法访问;private是不可见数据。
#include <iostream> #include <string> #include <iostream> #include <algorithm> using namespace std; class Base { public: int a{1}; protected: int b{2}; private: int c{3}; }; class Derive : private Base { public: void show() { cout << a << b << endl; } }; int main(int argc, char* argv[]) { Derive d; d.show(); return 0; }
继承中的构造和析构
子类是由父类成员叠加子类新成员而成。
构造顺序是 先父类再子类,析构顺序相反,如果有对象成员则是先父类再对象成员再子类,析构顺序相反。
#include <iostream> #include <string> #include <iostream> #include <algorithm> #include <fstream> using namespace std; class Other { public: Other() { cout << "Other" << endl; } ~Other() { cout << "~Other" << endl; } }; class Base { public: Base() { cout << "Base" << endl; } ~Base() { cout << "~Base" << endl; } }; class Derive : private Base { public: Derive() { cout << "Derive" << endl; } ~Derive() { cout << "~Derive" << endl; } Other o; }; int main(int argc, char* argv[]) { Derive d; return 0; }
继承中的同名变量和同名函数
同名变量
当父类和子类中成员变量同名时,默认会选择子类成员,如果想访问父类同名成员则必须加上父类的作用域
#include <iostream> #include <string> #include <iostream> #include <algorithm> #include <fstream> using namespace std; class Base { public: int num{10}; }; class Derive : public Base { public: int num{20}; }; int main(int argc, char* argv[]) { Derive d; cout << d.num << endl; cout << d.Base::num << endl; return 0; }
C++类和对象(下):https://developer.aliyun.com/article/1459443