继承
- 子类通过继承的方法可以调用到父类的功能
clacc CPeople //父类 基类
{
public:
cout << "学习";
};
class CChild : public CPeople // CChild是子类,继承CPeople父类
{
public:
void GoToSchool()
{
cout << "去学校" <<endl;
}
};
int main ( )
{
CChild XiaoMing ;
XiaoMing . study ; // XiaoMing是CChild类,CChild了类中没用定义study方法,但是他继承了CPeople方法,所以可以使用该方法。
}
继承中的运算符
- public:共有的,都可以使用
- private:纯私有,类内可见
- protected:受保护的,类内、子类可见
- 有public时为共有继承,对象在父类中是哪种运算符,在子类中也是哪种运算符。父类的私有成员也被子类继承了,在子类中依然是父类的私有成员,他可以继承,但是不能调用。
- 有protected的时候,继承之后,父类的public降级成protected,父类中的protected,private不变。
- 有private的时候,私有继承之后,父类对象的public和private成员都会变成private成员,父类的private成员还是访问不到
clacc CPeople //父类 基类
{
private :
void fun1()
{
}
protected:
void fun2()
{
}
public:
void fun3()
{
}
};
class CChild : public CPeople //有public时为共有继承
{
public:
int a;
};
int main()
{
CChild XiaoMing;
XiaoMing.fun3();
}
继承的构造函数调用顺序
构造函数的调用顺序:先父类,后子类
clacc CPeople //父类 基类
{
public :
cout << "people" ;
} ;
class CXiaoMing : public CPeople
{
public :
CXiaoMing ( )
{
cout << "XiaoMing" << endl ;
}
} ;
class CJob : public CXiaoMing
{
public :
CJob ( )
{
cout << "Job" << endl ;
}
} ;
int main ( )
{
CJob job ;
cout << job ;
}
// 会输出:people xiaoming job,顺序为先父类,后子类
继承的析构函数调用顺序
析构函数调用顺序与构造函数正好先反:先子类,后父类
从父类继承的成员属不属于子类对象
-
父类中的 所有非静态成员,属性 ,都会被子类继承下去,私有成员被编译器隐藏了,因此访问不到,但是确实被继承下去了,父类的三种属性变量都会在子类中占用空间
-
查看类的结构:
1. 利用开发人员命令提示符
2. cd 到类的路径下
3. 查看命令dir
4. 查看命令:cl /d1 reportSingleClassLayout类名(中文:报告单个类布局)(开头时CL空格/D一)
继承中同名的成员处理
当自身的成员与父类成员同名(仅同名就可以,有无参数不影响),同名的子类自身的成员会隐藏掉所有同名父类成员(包括同名不同参的),想要调用父类的成员可以s.farther ::a;加一个父类作用域,想调用子类中没有的同名不同参的成员也要加类名作用域
- 继承同名的静态成员子类处理方式
// 父类
class Base
{
public:
// 静态成员变量
static int m;
// 同名的静态成员函数
static void fun()
{
cout << "父类的fun"
}
};
int Son::m =100;
// 子类
class Son :public Base
{
public:
// 静态成员变量
static int m;
// 同名的静态成员函数
static void fun()
{
cout << "子类的fun"
}
};
int Base::m =200;
void test1()
{
Son s;
cout << s.m; // 返回子类200
cout << s.Base::m; // 加上父类的类名作用域后,返回父类100
void test2()
{
Son y;
// 1.通过对象访问
s.fun();
s.Base::fun();
// 2.通过类名访问
Son::fun();
// 3.子类通过类名的方式访问父类
Son::Base::fun();
}
}
多继承
语法: class 子类 :父类1 : 父类2 : 父类3
- 由于多继承会引发父类中同名成员,实际开发中不建议使用
多态
多态是一种编程思想,虚函数是实现这个思想的语法基础。
-
父类的一个指针,可以有多种执行状态,即多态。
-
多态是一种泛型编程思想:同样的代码实现不同的功能,宏观的体现是父类的指针调用子类函数CFather* fa = new CSon;
-
多态分为两种:
- 静态多态:函数重载和运算符重载属于静态多态复用函数名
- 动态多态:派生类和虚函数实现运行时的多态。
-
两种多态的区别:
- 静态多态的函数地址早绑定—编译阶段确定函数地址(父类引用指向子类对象时,会调用父类的对象;早绑定的地址即为父类的引用。)
- 动态多态的函数地址晚绑定—运行阶段确定函数地址(子类重写父类的虚函数。)
-
虚函数的关键词:virtual,有这个关键字表明这个函数是个虚函数,在父类的指针调用子类函数的时候,如果子类对象调用父类和子类都有同名的函数,则调用子类自己的函数。
-
变量是没有虚的,只有函数有虚函数。
class CFather
{
public:
// 父类的指针调用子类函数时,会调用父类的函数
void show()
{
cout<< "class father";
}
// 该函数是个虚函数,父类的指针调用子类函数时,会调用子类的函数
virtual void shuchu()
{
cout<< "fafafa";
}
};
class CSon : public CFather
{
int aa;
void show()
{
cout<< "class son";
}
void shuchu()
{
cout<< "sososo";
}
};
int main()
{
CFather* fa = new CSon //父类的指针调用子类函数
fa.show(); // 会调用父类的show函数,由定义的类型决定
fa.shuchu(); // 会调用子类的shuchu函数
free(fa);
}
- 覆盖是子类的对象名和父类的对象名是相同的,然后在子类的作用范围内,子类的对象就会把父类的对象给覆盖掉。
- 重写是针对虚函数,在覆盖的基础上,在父类的函数前面加上一个virtual就构成了一个虚函数,就构成了一个重写关系,子类重写父类。
纯虚函数和抽象类
在多态中通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为 ** 纯虚函数 **
- 纯虚函数的语法:virtual 返回值类型 函数名 (参数列表) = 0;
当类中有一个纯虚函数这个类被称为抽象类 。 - 抽象类的特点:
- 无法实例化对象
- 子类必须重写抽象类中纯虚函数,否则也属于抽象类。
class Base
{
public :
virtual void func = 0 ;
} ;
class Son1 :public Base
{
public :
};
class Son2 :public Base
{
public:
virtual void func()
{
cout << "Son2's func" << endl;
}
};
void test()
{
// 两个会报错,抽象类无法实例化对象
Base b;
new Base;
//由于Son1没有重写抽象类的纯虚函数,所以也是抽象类,无法实例化,会报错
Son1 a;
Base* base = new Son2;
base->func(); //就会输出Son2的func函数:Son2's func
}
虚析构函数与纯虚析构
父类指针,在析构时不会调用子类中的析构函数,导致子类如果有堆区属性,会出现内存泄露。
利用虚析构函数,把父类中的析构函数定义为虚函数,这样在子类中重写虚函数就可以避免内存泄漏的出现。
// 虚析构
virtual ~ Animal ( )
{
delete pt ;
}
// 纯虚析构
virtual ~ Animal ( ) = 0 ;

本文详细探讨了C++中的继承机制,包括公共、私有和受保护继承的区别,构造函数和析构函数的调用顺序,以及如何处理继承中的同名成员。此外,讲解了多继承和多态的概念,重点介绍了虚函数、纯虚函数、抽象类以及虚析构函数的应用,以解决内存泄露问题。
2737

被折叠的 条评论
为什么被折叠?



