知识点1【强化训练字符串类String】
知识点2【继承和派生的概述】(了解)1-2
继承的优点:减少代码的冗余 提高代码的重用性
知识点3【继承的格式】
继承方式分类:
父类个数分类:
注意:
案例1:公有继承 public
总结:
案例2:保护继承protected
总结: protected继承
案例3:私有继承 private
总结:private私有继承
总结:
知识点4【继承的内层结构】(了解)(vs studio)
知识点5【继承中的构造和析构的顺序】
总结:
知识点6【子类中 有父类、对象成员 构造和析构的顺序】
总结:(重要)
知识点7【详解 子类中的构造】
1、子类会默认调用 父类的 无参构造
2、子类 必须显示 使用初始化列表 调用 父类的有参构造
知识点8【父类和子类的同名 成员变量 处理】
1、当 父类和子类 成员变量同名时 在子类就近原则 选择本作用域的子类成员
2、如果在子类中 必须使用父类中的同名成员 必须加上父类的作用域。
3、子类可以借助 父类的公有方法 间接的操作 父类的私有数据(不可见的数据)
知识点9【父类和子类的同名 成员函数 处理】
案例:1子类继承父类所有成员函数 和成员变量
案例2:子类和父类 同名成员函数
知识点1【强化训练字符串类String】
mystring.h
#ifndef
MYSTRING_H #define MYSTRING_H #include using namespace std;
class MyString { friend ostream&
operator<<(ostream &out, MyString &ob); friend istream&
operator>>(istream &in, MyString &ob); private: char *str; int size; public: MyString(); MyString(const char *str); MyString(const MyString &ob); ~MyString(); int Size(void); //重载[] char& operator[](int index); //重载= 参数是对象 MyString&
operator=(const MyString &ob); //重载= 参数是字符串常量
const char * MyString&
operator=(const char *str); //重载+运算符
MyString& operator+(const MyString &ob); MyString& operator+(const char
*str); //重载运算符 bool operator(const
MyString &ob); bool
operator==(const char *str); }; #endif // MYSTRING_H
mystring.cpp
#include
“mystring.h” #include<string.h> #include using
namespace std; MyString::MyString() {
this->str = NULL;
this->size = 0;
cout<<“无参构造”<<endl; } MyString::MyString(const char *str) { cout<<“char *构造函数”<<endl; //申请空间 this->str = new
char[strlen(str)+1]; //拷贝字符串 strcpy(this->str,
str); //更新size this->size = strlen(str); } MyString::MyString(const MyString &ob)
{ cout<<“拷贝构造函数”<<endl; //申请空间 this->str = new
char[strlen(ob.str)+1]; //拷贝字符串 strcpy(this->str,
ob.str); //更新size this->size =
ob.size; } MyString::~MyString() { cout<<“析构函数”<<endl;
if(this->str != NULL)
{ delete []
this->str; this->str =
NULL; } } int MyString::Size() { return this->size; } char& MyString::operator[](int
index)//index表示数组的下标 { //判断下标是否合法 if(index >=0 && index <
this->size) { return this->str[index]; }
else { cout<<“index无效”<<endl; } } MyString &MyString::operator=(const
MyString &ob) { //将ob.str拷贝到 this->str里面 //1、将this->str指向的旧空间 释放掉 if(this->str != NULL) {
delete [] this->str;
this->str = NULL; } //根据ob.str的大小申请空间 this->str = new
char[ob.size+1]; strcpy(this->str,
ob.str); this->size =
ob.size; return *this; } MyString &MyString::operator=(const char
*str) { //1、将this->str指向的旧空间
释放掉 if(this->str
!= NULL) { delete [] this->str; this->str = NULL; }
this->str = new char[strlen(str)+1]; strcpy(this->str, str); this->size = strlen(str); return *this; } MyString& MyString::operator+(const
MyString &ob) { //this 指向的是str5 ob是str6的别名
//计算将来两个字符串拼接后的长度 int newSize = this->size + ob.size
+1; char *tmp_str = new
char[newSize]; //清空tmp_str所指向的空间 memset(tmp_str,0,newSize); //先将this->str拷贝到 tmp_str中 然后将ob.str追加到tmp_str中
strcpy(tmp_str,this->str);
strcat(tmp_str,ob.str);
static MyString newString(tmp_str);
//释放tmp_str指向的临时空间 if(tmp_str != NULL) {
delete [] tmp_str; tmp_str
= NULL; } return newString; } MyString &MyString::operator+(const char
*str) { //计算将来两个字符串拼接后的长度 int newSize =
this->size + strlen(str) +1; char
*tmp_str = new char[newSize]; //清空tmp_str所指向的空间 memset(tmp_str,0,newSize); //先将this->str拷贝到 tmp_str中 然后将str指向的字符串 追加到tmp_str中
strcpy(tmp_str,this->str);
strcat(tmp_str,str); static
MyString newString(tmp_str); //释放tmp_str指向的临时空间 if(tmp_str != NULL) {
delete [] tmp_str; tmp_str
= NULL; } return newString; } bool MyString::operator==(const MyString
&ob) { if( (strcmp(this->str,
ob.str) == 0) && (this->size == ob.size)) {
return true; }
return false; } bool
MyString::operator==(const char *str) {
if( (strcmp(this->str, str) == 0) && (this->size ==
strlen(str))) { return true; }
return false; } ostream& operator<<(ostream &out, MyString
&ob) { out<<ob.str;//访问了ob中的私有数据 必须设置成友元 return out; } istream& operator>>(istream
&in, MyString &ob) { //记得将原有的数据清楚 if(ob.str != NULL) {
delete [] ob.str; ob.str
=NULL; } //获取键盘输入的字符串 char buf[1024]="";//临时buf in >> buf;//先得到键盘输入的数据 然后根据buf的实际大小
开辟空间 ob.str = new
char[strlen(buf)+1]; strcpy(ob.str,
buf); ob.size = strlen(buf); return in; }
main.cpp
#include
#include"mystring.h" using namespace std; int main(int argc, char *argv[]) { MyString str1(“hehe”); //自定义对象 必须重载<<
(普通全局友元函数实现)
cout<<str1<<endl;
cout<<"size = "<<str1.Size()<<endl; //自定义对象 必须重载>>
(普通全局友元函数实现)
cin>>str1;
cout<<str1<<endl;
cout<<"size = “<<str1.Size()<<endl; MyString str2(“hello
class”); //重载[]运算符
cout<<str2[1]<<endl;
//重载[]运算符 返回值必须是左值 才能写操作 //重载[]运算符 的返回值必须是引用 str2[1] =‘E’; cout<<str2<<endl; MyString str3(“hello
str3”); cout<<“str3:”<<str3<<endl; //将对象str2 赋值
给str3 //(默认赋值语句
浅拷贝) //必须重载=运算符(成员函数完成) str3 = str2;
cout<<“str3:”<<str3<<endl; MyString str4(“hello str4”);
cout<<“str4:”<<str4<<”, size =
“<<str4.Size()<<endl;
//必须重载=运算符(成员函数完成) str4=“hello string”;
cout<<“str4:”<<str4<<”, size =
"<<str4.Size()<<endl;
//重载+运算符 MyString str5(“我爱大家”); MyString
str6(“我爱千锋”); cout<<str5+str6<<endl; MyString str7(“大家爱我”); cout<<
str7+“千锋爱我”<<endl; //重载==运算符 MyString
str8(“hehe”); MyString
str9(“haha”); if(str8 == str9) {
cout<<“相等”<<endl; }
else { cout<<“不相等”<<endl; } if(str8 == “hehe”) {
cout<<“相等”<<endl; }
else { cout<<“不相等”<<endl; } return 0; }
运行结果:
知识点2【继承和派生的概述】(了解)1-2
继承的优点:减少代码的冗余 提高代码的重用性
知识点3【继承的格式】
派生类定义格式:
Class 派生类名 : 继承方式 基类名{ //派生类新增的数据成员和成员函数 }; class
子类: 继承方式 父类名{ //子类新增的数据成员和成员函数 };
继承方式分类:
public : 公有继承 (重要)
private : 私有继承
protected : 保护继承
父类个数分类:
单继承:指每个派生类只直接继承了一个基类的特征 (一个父类 派生出 一个子类)
多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特征(多个父类 派生出 一个子类)
注意:
子类继承父类,子类拥有父类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在子类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限。
案例1:公有继承 public
//设置一个父类 class Base { public: int
a; private: int b; protected: int
c; }; //设置一个子类
class Son:public Base { public: //父类中的public数据
在子类中 也是public
//父类中的private数据 在子类中 是不可见的 //父类中的protected数据 在子类中 是protected的 //子类的内部 void showSon() {
//b = 200;//不能直接访问 c =300;//在子类 内部是可以访问的 } };
void test01() { //子类的外部 Son ob; ob.a = 100; cout<<"父类中的public数据a =
"<<ob.a<<endl;
//ob.b = 200;//在子类外 访问不了 //ob.c = 200;//在子类外 访问不了 }
总结:
父类中的public数据 在子类中 也是public
父类中的private数据
在子类中 是不可见的
父类中的protected数据 在子类中 是protected的
(public 继承 父类中的私有数据 在子类 不可见 其他保持原样)
案例2:保护继承protected
//保护继承 class Son1:protected Base { private: public:
//父类中的public数据 在子类中 也是protected //父类中的private数据
在子类中 是不可见的 //父类中的protected数据 在子类中 是protected的 //子类的内部 void showbase(){ a = 100;//子类内部可访问 //b = 200;//不能直接访问 c = 300;//子类内部可访问 } }; void test02() { Son1 ob; //ob.a;//子类外不可访问 //ob.b;//子类外不可访问 //ob.c;//子类外不可访问 }
总结: protected继承
父类中的public数据
在子类中 也是protected
父类中的private数据 在子类中 是不可见的
父类中的protected数据
在子类中 是protected的
(保护继承 父类的私有数据 在子类中 不可见 其他数据 都变保护)
案例3:私有继承 private
//保护继承 class Son2:private Base { private:
public: //父类中的public数据
在子类中 也是private //父类中的private数据
在子类中 是不可见的 //父类中的protected数据 在子类中 是private的 //子类的内部 void showbase(){ a = 100;//子类内部可访问 //b = 200;//不能直接访问 c = 300;//子类内部可访问 } }; void test03() { Son2 ob; //ob.a;//子类外不可访问 //ob.b;//子类外不可访问 //ob.c;//子类外不可访问 }
总结:private私有继承
父类中的public数据 在子类中 也是private
父类中的private数据
在子类中 是不可见的
父类中的protected数据
在子类中 是private的
(私有继承 父类中的私有数据在子类中 不可见 其他变成私有)
总结:
不管啥继承方式:父类中的私有数据在 子类中不可见
知识点4【继承的内层结构】(了解)(vs studio)
class Base {
public: int a; protected: int b; private: int c; };
class Son :public Base { public:
int d; int e; }; int main(int
argc, char* argv[]) { cout <<
sizeof(Son) << endl; return 0;
}
步骤:
cl /d1
reportSingleClassLayoutSon test.cpp
Son类的布局:
知识点5【继承中的构造和析构的顺序】
class Base {
public: Base()
{ cout<<“父类的无参构造函数”<<endl; } ~Base()
{ cout<<“父类中的析构函数”<<endl; } };
class Son:public Base { public:
Son() { cout<<“子类的无参构造”<<endl; } ~Son()
{ cout<<“子类中的析构函数”<<endl; } }; void
test01() { Son ob1; }
运行结果:
总结:
构造顺序: 父类(基类)构造 ------> 子类(派生类)构造
析构顺序:子类(派生类)析构------> 父类 (基类) 析构
知识点6【子类中 有父类、对象成员 构造和析构的顺序】
父类的构造和析构 对象成员的构造和析构 子类自身的构造和析构
总结:(重要)
class Other
{ public: Other() {
cout<<“对象成员的构造函数”<<endl; }
~Other() { cout<<“对象成员的析构函数”<<endl; } };
class Base { public: Base() {
cout<<“父类的无参构造函数”<<endl; }
~Base() { cout<<“父类中的析构函数”<<endl; } };
class Son:public Base { public:
Son() { cout<<“子类的无参构造”<<endl; } ~Son()
{ cout<<“子类中的析构函数”<<endl; } Other ob;//对象成员 }; void test01() { Son ob1; }
运行结果:
知识点7【详解 子类中的构造】
1、子类会默认调用 父类的 无参构造
2、子类 必须显示 使用初始化列表 调用 父类的有参构造
调用形式:父类名称。
Son(int
a,int b):Base(a),b(b) { //this->b
= b; }
class Base {
private: int a; public: Base()
{ cout<<“父类的无参构造函数”<<endl; } Base(int a) {
this->a = a;
cout<<“父类的有参构造函数”<<endl; }
~Base() { cout<<“父类中的析构函数”<<endl; } };
class Son:public Base { private: int
b; public: Son() {
cout<<“子类的无参构造”<<endl; }
Son(int b) { this->b = b; cout<<“子类的有参构造函数int”<<endl;
} //子类必须用 初始化列表 显示的调用父类的有参构造 //父类名称(参数)
Son(int a,int b):Base(a)//显示的调用父类的有参构造 {
this->b = b;
cout<<“子类的有参构造函数 int
int”<<endl; } ~Son()
{ cout<<“子类中的析构函数”<<endl; } };
void test01() { //子类 默认 会调用 父类的无参构造 //Son ob1(10); //子类必须用 初始化列表 显示的调用父类的有参构造 //父类名称+() Son ob2(10,20); }
运行结果:
案例提高:
如果父类有参构造:
Base(int a, int data) { this->a = a; this->data = data; cout<<“父类的有参构造函数”<<endl; }
子类想调用 父类有参构造:
//子类必须用
初始化列表 显示的调用父类的有参构造 //父类名称(参数) Son(int a,int b, int c):Base(a,c),b(b)//显示的调用父类的有参构造 { //this->b = b; cout<<“子类的有参构造函数 int int”<<endl; }
知识点8【父类和子类的同名 成员变量 处理】
1、当 父类和子类 成员变量同名时 在子类就近原则 选择本作用域的子类成员
2、如果在子类中 必须使用父类中的同名成员 必须加上父类的作用域。
class Base
{ //父类的私有数据 一旦涉及继承 在子类中不可见 public: int num; public: Base(int num) {
this->num = num;
cout<<“Base有参构造int”<<endl; }
~Base() { cout<<“析构函数”<<endl; }
}; class Son:public Base { private: int num; public: Son(int num1,int num2):Base(num1) {
this->num = num2; cout<<“有参构造int int”<<endl;
} ~Son() {
cout<<“析构函数”<<endl; }
void showNum(void) { //如果在子类中 必须使用父类中的同名成员 必须加上父类的作用域 cout<<"父类中的num = "<<Base::num<<endl; //当 父类和子类 成员变量同名时 在子类就近原则 选择本作用域的子类成员 cout<<"子类中的num = "<<num<<endl; } };
void test01() { Son
ob1(10,20); ob1.showNum(); }
运行结果:
3、子类可以借助 父类的公有方法 间接的操作 父类的私有数据(不可见的数据)
class Base
{ private: int num;//父类的私有数据 一旦涉及继承 在子类中不可见 public: Base(int
num) { this->num = num; cout<<“Base有参构造int”<<endl;
} ~Base() {
cout<<“析构函数”<<endl; }
int getNum(void) { return num; } };
class Son:public Base { private:
int num; public: Son(int num1,int
num2):Base(num1) { this->num = num2; cout<<“有参构造int int”<<endl;
} ~Son() {
cout<<“析构函数”<<endl; }
void showNum(void) { //如果在子类中 必须使用父类中的同名成员 必须加上父类的作用域 cout<<"父类中的num = "<<getNum()<<endl; //当 父类和子类 成员变量同名时 在子类就近原则 选择本作用域的子类成员 cout<<"子类中的num = "<<num<<endl; } };
void test01() { Son
ob1(10,20); ob1.showNum(); }
运行结果:
知识点9【父类和子类的同名 成员函数 处理】
案例:1子类继承父类所有成员函数 和成员变量
class Base {
public: void func(void) {
cout<<“父类中的void
func”<<endl; } void func(int a) {
cout<<"父类中的int func a =
"<<a<<endl; }
}; class Son:public Base { public: };
void test01() { //为啥构造和析构除外?父类的构造和析构 只有父类自己知道该怎么做(构造和析构 系统自动调用) //子类会继承父类所有成员函数(构造和析构函数除外) 和成员变量
Son ob1; ob1.func();//访问的是父类的void func(void)
ob1.func(10);//访问的是父类的func(int a) }
案例2:子类和父类 同名成员函数
class Base {
public: void func(void) {
cout<<“父类中的void
func”<<endl; } void func(int a) {
cout<<"父类中的int func a =
"<<a<<endl; }
}; class Son:public Base { public: //一旦子类 实现了 父类的同名成员函数 将屏蔽所有父类同名成员函数 void func(void) {
cout<<“子类中voidfunc”<<endl; } };
void test01() { //为啥构造和析构除外?父类的构造和析构 只有父类自己知道该怎么做(构造和析构 系统自动调用) //子类会继承父类所有成员函数(构造和析构函数除外) 和成员变量
Son ob1; ob1.func(); //ob1.func(10);//err //一旦子类 实现了 父类的同名成员函数
将屏蔽所有父类同名成员函数 //如果用户
必须要调用父类 的同名成员函数 必须加作用域
ob1.Base::func();//调用父类的void func ob1.Base::func(10);//调用父类的int func } int main(int argc, char *argv[]) { test01(); return 0; }
运行结果: