C++面向对象学习

文章目录

  • c++ 11特性
    • auto和decltype类型识别
    • nullptr引入
    • 智能指针
    • lambda函数
    • 移动语义
  • 内存管理
    • 分区
    • 虚函数和纯虚函数
    • 内存池
  • 虚函数
  • 继承方法

c++ 11特性

auto和decltype类型识别

auto无法识别const顶层和引用,其他类型可识别。
decltype可识别const顶层和引用,只使用类型而不使用值。

nullptr引入

c: null = (void*)0
c++: null = 0
问题:因为C++中允许有函数重载,无法区分指针和整数0
解决:c++引入nullptr仅可表示指针类型而不会转成整数类型

智能指针

shared_ptr: 共享同一个对象,并计数对象的引用次数,引用次数为0时,对象销毁。
unique_ptr:仅独享一个对象,禁止使用拷贝语义而只能使用移动语义。
weak_ptr:配合shard_ptr仅引用而不增加对象的引用次数,依次打破shard_ptr的环形引用(case 1)导致的内存无法释放。若内存shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。解决方法具体实现参考链接

lambda函数

格式:[capture] (parameters) mutable ->return-type {statement};
[捕获lambda函数外部的变量 =:值传递 &:引用 this:值传递捕获当前对象] (参数列表) mutable ->返回类型 {函数体}
默认情况下lambda函数是const函数,其中不允许改变内容,可用mutable取消常量性。

移动语义

参考链接
左值可以理解为有实际存储地址的变量,反之右值没有,用完即销。
在类赋值时,若将对象a的值赋给b,然后接下来仅使用对象b而不再使用对象a,则可使用移动语义:直接将a的成员指针全指向b的成员指针,而不必进行普通的深拷贝,浪费时间和空间。

#include 
#include 
#include 
using namespace std;

class String {
public:
    char* str;
    int len;
        String(char *_str, int _len) {
        str = new char[_len + 1];
        len = _len;
        strncpy(str, _str, len + 1);
    }
    String(){}
    //普通的=运算符重载 this和x均存储相同的内容 但是需要更多的时间进行复制 并且浪费存储空间
    String& operator=(const String& x) {
        if(this != &x){
            delete []str;
            str = new char[x.len + 1];
            len = x.len;
            strncpy(str, x.str, len + 1);
        }
        return *this;
    }
    //参数类型为右值引用 具体功能需要自己实现
    String& operator=(String&& x){
        if(this != &x){
            delete []str;
            str = x.str;
            len = x.len;
            x.str = nullptr;
        }
        return *this;
    }

};


int main() {
    char m[] = "abc", n[] = "efg";
    String x(m, 3), y(n, 3);
    //将一个左值强制转化为右值引用,继而可以通过右值引用使用该值
    x = move(y); 
    cout << x.str << endl;
    return 0;
}

内存管理

分区

堆区
栈区
常量区
静态/全局变量区
代码区

虚函数和纯虚函数

参考链接
虚函数在函数前添加virtual关键字,则基类可访问派生类的该函数。若基类在虚函数后添加final,则表示该函数不可重写。若派生类在虚函数后添加override,则表示该虚函数是重写基类中的一个函数,需要保证函数签名(const/函数名/参数类型等)均一致。

 
#include
using namespace std;
 
class A {
public:
	virtual void vfunc1() { cout << "A::vfunc1()" << endl; };
	void func1() { cout << "A::func1()" << endl; };
};
 
class B :public A {
public:
	virtual void vfunc1() override { cout << "B::vfunc1()" << endl; };
	void func2() { cout << "B::func2()" << endl; };
};

// 输出:
// B::vfunc1()
// A::func1()
int main() {
    A *x = new B;
    x->vfunc1();
    x->func1();
	return 0;
}

若一个类有纯虚函数 vitural returnType funcionName(parm) = 0;则此类为抽象类,不能创建对象。

若一个类有虚函数,则该类的对象会多一个8字节的虚指针,指向该类的虚函数表。虚函数表中存放着若干该类从父类和本类的虚函数指针。当然若重写了父类的虚函数,则父类的虚函数指针会被新的函数指针覆盖。

内存池

参考链接

  • 动态绑定:通过基类的引用或指针调用虚函数发生动态绑定,该调用取决于基类指针指向的对象类型,可以是派生类或基类,对虚函数版本的调用在运行时确定。

派生类要访问基类的protected成员,只能通过派生类对象进行访问,派生类无法直接访问。

虚函数

  • 虚函数返回值:若基类的虚函数返回值是基类的引用或指针,则派生类虚函数返回值可以是基类或派生类的引用或指针

  • 静态类型和动态类型:在编译过程,编译器将基类指针或引用的对象均当作基类对象,而只有到了运行过程,才可知其指向对象的类型,也只有此时才确定调用哪个virtual函数。因此基类指针或引用是静态类型,而其指向的对象类型是动态类型。c语言编译流程如下:
    C++面向对象学习_第1张图片

  • 参数缺省值:虚函数参数的缺省值在编译时确定,因此和动态类型无关只和静态类型有关。若基类的参数缺省值和派生类不同,且在调用其虚函数时没有传入参数,则基类的参数缺省值会传给派生类的虚函数版本。

 
class A {
public:
	
	virtual int vfunc1(int x = 1) {
		return x;
 	}
};
 
class B :public A {
public:
	virtual int vfunc1(int x = 2) {
		return x;
 	}
};
int main() {
    A *x = new B;
    cout << x->vfunc1();
	//输出1
	return 0;
}

继承方法

基类权限\继承方式 public继承 protected继承 private 继承
public public protected private
protected protected protected private

你可能感兴趣的:(c++,学习)