cpp面向对象
面向对象编程
类
C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,用户定义的类型。
class Student { int i; //默认 private public: Student(int i,int j,int k):i(i),j(j),k(k){}; //构造方法 ~Student(){}; //析构方法 private: int j; protected: int k; }; Student student(1,2,3); //调用构造方法 栈 //出方法释放student 调用析构方法 //动态内存(堆) Student *student = new Student(1,2,3); //释放 delete student; student = 0;
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行(不需要手动调用)。
private:可以被该类中的函数、友元函数访问。 不能被任何其他访问,该类的对象也不能访问。
protected:可以被该类中的函数、子类的函数、友元函数访问。 但不能被该类的对象访问。
public:可以被该类中的函数、子类的函数、友元函数访问,也可以被该类的对象访问。
拷贝构造函数
class Student { int i; //默认 private public: Student(int i,int j,int k):i(i),j(j),k(k){}; //拷贝构造函数(隐式的会有拷贝构造函数,我们也可以自己实现) Student(const Student &student){ this->i = sudent.i; } //构造方法 ~Student(){}; //析构方法 private: int j; protected: int k; }; Student student(1,2,3); //调用构造方法 栈 //出方法释放student 调用析构方法 // Student s; // s = student; // 这样赋值是不会调用自定义拷贝构造函数,但是会调用默认赋值 Student s = student;//s的属性值跟student的一样 //动态内存(堆) Student *student = new Student(1,2,3); Student *student2 = student;//不会调用自定义拷贝构造函数 //释放 delete student; student = 0;
常量函数
函数后写上const,表示不会也不允许修改类中的成员。
class Student { int i; public: Student() {} ~Student() {} // 常量函数 void setName(char* _name) const { //错误 不能修改name 去掉const之后可以 name = _name;// 编译不通过 } private: int j; char *name; protected: int k; };
友元
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
友元函数
class Student { int i; public: Student() {} ~Student() {} void setName(char* _name) { name = _name; } //定义友元函数(声明,没有实现) friend void printName(Student *student); private: int j; char *name; protected: int k; }; //友元函数的实现 void printName(Student *student) { //能够使用student中私有的name属性 cout << student->name << endl; } Student *student = new Student; student->setName("张三"); printName(student);
友元类
class Student { int i; public: Student() {} ~Student() {} void setName(char* _name) { name = _name; } friend void printName(Student *student); //友元类 friend class Teacher; private: int j; char *name; protected: int k; }; class Teacher { public: void call(Student *student) { //能够使用student中私有的name属性 cout << "call:" << student->name << endl; } };
静态成员
和Java一样,可以使用static来声明类成员为静态的
当我们使用静态成员属性或者函数时候 需要使用 域运算符 ::
//Instance.h #ifndef INSTANCE_H #define INSTANCE_H class Instance { public: static Instance* getInstance(); private: static Instance *instance; }; #endif //Instance.cpp #include "Instance.h" Instance* Instance::instance = 0; Instance* Instance::getInstance() { //C++11以后,编译器保证内部静态变量的线程安全性 if (!instance) { instance = new Instance; } return instance; }
// C++ static关键字。 正确的写法 /** * 静态的总结: * 1.可以直接通过类名::静态成员(字段/函数) * 2.静态的属性必须要初始化,然后再实现(规则) * 3.静态的函数只能取操作静态的属性和方法(Java) */ #include <iostream> using namespace std; class Dog { public: char * info; int age; // 先声明 static int id; static void update() { id += 100; // 报错:静态函数不能调用非静态函数(Java) // update2(); } void update2() { id = 13; } }; // 再实现 int Dog::id = 9; int main() { Dog dog; dog.update2(); // 普通函数 Dog::update(); // 静态函数 dog.update(); // 对象名.静态函数(一般都是使用::调用静态成员,这种方式可以 知道就行) cout << Dog::id << endl; return 0; }
重载函数
C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分为函数重载和运算符重载。
函数重载
void print(int i) { cout << "整数为: " << i << endl; } void print(double f) { cout << "浮点数为: " << f << endl; }
操作符重载
C++允许重定义或重载大部分 C++ 内置的运算符
函数名是由关键字 operator 和其后要重载的运算符符号构成的
重载运算符可被定义为普通的非成员函数或者被定义为类成员函数
成员函数
class Test1 { public: Test1(){} //定义成员函数进行重载 //返回对象 调用拷贝构造函数 释放函数内 t 对象 //引用类型(Test1&) 没有复制对象 返回的是 t 对象本身 t会被释放 所以会出现问题(数据释放不彻底就不一定) // 可以输出 t 与 t3 地址查看 Test1 operator+(const Test1& t1) { Test1 t; t.i = this->i + t1.i; return t; } //拷贝构造函数 (有默认的) Test1(const Test1& t){ //浅拷贝 this->i = t.i; cout << "拷贝" << endl; //如果动态申请内存 需要深拷贝 }; int i; }; Test1 t1; Test1 t2; t1.i = 100; t2.i = 200; //发生两次拷贝 // C++真正的临时对象是不可见的匿名对象 //1、拷贝构造一个无名的临时对象,并返回这个临时对象 //2、由临时对象拷贝构造对象 t3 //语句结束析构临时对象 Test1 t3 = t1 + t2; cout << t3.i << endl;
Xcode上玩,使用的g++编译器会进行 返回值优化(RVO、NRVO) 从而看不到拷贝构造函数的调用。
可以加入 "-fno-elide-constructors" 取消GNU g++优化
对windows vs编译器cl.exe无效,VS Debug执行RVO,Release执行NRVO
RVO(Return Value Optimization):消除函数返回时创建的临时对象
NRVO(Named Return Value Optimization):属于 RVO 的一种技术, 直接将要初始化的对象替代掉返回的局部对象进行操作。
非成员函数
class Test2 { public: int i; }; //定义非成员函数进行 + 重载 Test2 operator+(const Test2& t21, const Test2& t22) { Test2 t; t.i = t21.i + t22.i; return t; } Test2 t21; Test2 t22; t21.i = 100; t22.i = 200; Test2 t23 = t21 + t22; cout << t23.i << endl;
允许重载的运算符
void *operator new (size_t size) { cout << "新的new:" << size << endl; return malloc(size); } void operator delete(void *p) { //释放由p指向的存储空间 cout << "新的delete" << endl; free(p); } ... ...
继承
class A:[private/protected/public] B
默认为private继承
A是基类,B称为子类或者派生类
方式 | 说明 |
---|---|
public | 基类的public、protected成员也是派生类相应的成员,基类的private成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。 |
protected | 基类的公有和保护成员将成为派生类的保护成员 |
private | 基类的公有和保护成员将成为派生类的私有成员 |
class Parent { public: void test() { cout << "parent" << endl; } }; class Child : Parent { public: void test() { // 调用父类 方法 Parent::test(); cout << "child" << endl; } };
多继承
一个子类可以有多个父类,它继承了多个父类的特性。
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
多态
多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
静态多态(静态联编)是指在编译期间就可以确定函数的调用地址,通过函数重载和模版(泛型编程)实现
动态多态(动态联编)是指函数调用的地址不能在编译器期间确定,必须需要在运行时才确定 ,通过继承+虚函数 实现
虚函数
class Parent { public: void test() { cout << "parent" << endl; } }; class Child :public Parent { public: void test() { cout << "child" << endl; } }; Parent *c = new Child(); // 编译期间 确定c 为 parent 调用parent的test方法 c->test();//parent //修改Parent为virtual 虚函数 动态链接,告诉编译器不要静态链接到该函数 virtual void test() { cout << "parent" << endl; } //动态多态 调用Child的test方法 c->test();//child
构造函数任何时候都不可以声明为虚函数
析构函数一般都是虚函数,释放先执行子类再执行父类
当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数
虚析构函数
1、虚析构函数的作用:避免内存泄漏。
使用虚析构函数在删除指向子类对象的基类指针时,可以调用子类的析构函数使子类占用的堆内存释放,从而防止内存泄漏。
2、使用总结
(1)在基类的析构函数前加上virtual关键字,那么就是虚析构函数。
(2)当基类中的析构函数声明为虚析构函数时,派生类开始从基类继承。
(3)基类的指针指向派生类的对象时,delete基类的指针时,先调用派生类的析构函数,再调用基类中的析构函数。
#include <iostream> using namespace std; ///基类 class Base{ public: Base(){}; virtual ~Base(){ //1、定义虚析构函数 cout << "delete Base\n"; }; virtual void DoSomething(){ cout << "Do Something in class Base!\n"; }; }; ///派生类 class Derived: public Base{ //2、子类继承 public: Derived(){}; ~Derived(){ cout << "delete Derived\n"; }; void DoSomething(){ cout << "Do Something in Derived\n"; }; }; int main(){ Base *b = new Derived; //3、基类指针指向子类 b->DoSomething(); delete b; return 0; }
纯虚函数
class Parent { public: //纯虚函数 继承自这个类需要实现 抽象类型 virtual void test() = 0; }; class Child :public Parent { public: void test(){} };
纯虚函数是在声明虚函数时被“初始化”为0的函数。
纯虚函数没有函数体
纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。
凡事包含纯虚函数的类都是抽象类
模板
模板是泛型编程的基础
函数模板
函数模板能够用来创建一个通用的函数。以支持多种不同的形參。避免重载函数的函数体反复设计。
template <typename T> T max(T a,T b) { // 函数的主体 return a > b ? a : b; } //代替了 int max(int a,int b) int max(float a,float b)
类模板(泛型类)
为类定义一种模式。使得类中的某些数据成员、默写成员函数的參数、某些成员函数的返回值,能够取随意类型
常见的 容器比如 向量 vector
或 vector 就是模板类
template<class E,class T> class Queue { public: T add(E e,T t){ return e+t; } }; Queue<int,float> q; q.add(1,1.1f) = 2.1f
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!