类的属性
- 用public修饰的成员函数和属性能在类的内部和类的外部使用
- 用private修饰的成员函数和属性能在类的内部使用,但不能在类的外部使用
- 用protect修饰的成员函数和属性能在类的内部使用,但不能在类的外部使用
我们可以做一个比较,开放性public > protect> private。
#include <iostream>
using namespace std;
//封装 属性和操作放在一个类里面
//面向过程加工处理对象 函数
//继承 代码复用 复用以前的代码
//代码复用的层次
//1.可以复用以前的代码
//2.80年代的一个代码,可以调用2015年人写的代码
class Parent
{
public:
int a; //老爹的名字
protected:
int b; //对一部分保密 老爹的银行密码
private:
int c; //老爹的情人
public:
Parent(int a = 0, int b = 0, int c = 0)
{
this->a = a;
this->b = b;
this->c = c;
}
void maprint()
{
cout << "我是父亲!" << endl;
}
};
class Child : public Parent
{
public:
int getA()
{
return a;
}
int getB()
{
return b;
}
//在子类的内部不能私用父类的私有成员和函数
int getC()
{
return c;
}
};
//C++中类成员的返回级别
//用public修饰的成员函数和属性能在类的内部和类的外部使用
//用private修饰的成员函数和属性能在类的内部使用,但不能在类的外部使用
//用protect修饰的成员函数和属性能在类的内部使用,但不能在类的外部使用
void Demo01()
{
Parent p1;
p1.maprint();
Child c1;
c1.maprint();
//子类从父类中公有继承protect修饰的成员函数和属性在类的外部不能访问
//
c1.b;
c1.c;
}
void main()
{
Demo01();
system("pause");
}
继承基本概念
面向对象中的继承指类之间的父子关系
(1)子类拥有父类的所有成员变量和成员函数
(2)子类就是一种特殊的父类
(3)子类对象可以当作父类对象使用(赋值兼容性原则)
(4)子类可以拥有父类没有的方法和属性
继承中的构造和析构
类的继承方式对子类对外访问属性影响
(1)看调用语句,这句话是写在子类的内部、外部
(2)看子类如何从父类继承 (public,protected,private)
(3)看父类中的访问级别(public,protected,private)
C++中子类对外访问属性表
总结:不同的继承方式可能改变继承成员的访问属性
记忆:public继承不会改变父类对外访问属性;private继承会改变父类对外访问属性为private;protected继承会部分改变父类对外访问属性。
其实很好记忆,子类从父类得到的变量和函数的访问属性不会高于子类继承父类的方式,如果子类以protected方式继承父类,那么子类中得到的父类的public属性的方法和变量将会降阶到protect,父类中等于和低于protected属性的方法和变量不会改变。
同理类推。
#include <cstdlib>
#include <iostream>
using namespace std;
class A
{
private:
int a;
protected:
int b;
public:
int c;
A()
{
a = 0;
b = 0;
c = 0;
}
void set(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
};
class B : public A
{
public:
void print()
{
//cout<<"a = "<<a; //err
cout << "b = " << b;
cout << "c = " << endl;
}
};
class C : protected A
{
public:
void print()
{
//cout << "a = " << a; //err
cout << "b = " << b;
cout << "c = " << endl;
}
};
class D : private A
{
public:
void print()
{
cout << "a = " << a; //err
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
};
int main(int argc, char *argv[])
{
A aa;
B bb;
C cc;
D dd;
aa.c = 100; //ok
bb.c = 100; //ok
//cc.c = 100; //err 类的外部是什么含义
//dd.c = 100; //err
aa.set(1, 2, 3);
bb.set(10, 20, 30);
//cc.set(40, 50, 60); //err
//dd.set(70, 80, 90); //err
bb.print();
cc.print();
dd.print();
system("pause");
return 0;
}
//1 看访问父类属性(函数)这句话是写在了子类的内部、外部
//2 看子类如何从父类继承 (public、protected、private)
//3 看父类中的访问级别(public、protected、private)
继承模型
(1)在子类对象构造的时,需要调用父类构造函数对其继承得来的成员进行初始化。
(2)在子类对象析构的时,需要调用父类析构函数对其继承得来的成员进行清理。
继承与组合混搭情况下,构造和析构调用原则
原则:先构造父类,再构造成员变量,最后构造自己
先析构自己,在析构成员变量,最后析构父类
#include <cstdlib>
#include <iostream>
using namespace std;
/*
1.父类的属性,应该有父类的构造函数去初始化
2.子类新增加的属性,应该有子类的构造函数完成;
==>父子分工,很明确
*/
//在子类对象构造的时,需要调用父类构造函数对其继承得来的成员进行初始化
//在子类对象析构的时,需要调用父类析构函数对其继承得来的成员进行清理
class Parent01
{
public:
Parent01(const char* s)
{
cout << "Parent01()" << " " << s << endl;
}
~Parent01()
{
cout << "~Parent01()" << endl;
}
};
class Child02 : public Parent01
{
public:
Child02() : Parent01("Parameter from Child!")
{
cout << "Child02()" << endl;
}
~Child02()
{
cout << "~Child02()" << endl;
}
};
void run()
{
Child02 child;
}
int main(int argc, char *argv[])
{
run();
system("pause");
return 0;
}
#include "iostream"
using namespace std;
class A
{
public:
int a;
int b;
A(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
void add()
{
a = a + 10;
}
void print()
{
cout << "a = " << a << ",b = " << b << endl;
}
};
class B : public A
{
public:
void add()
{
a++;
}
int b; //在子类中也可以定义和父类中同名的变量和函数,在子类中调用该变量和函数是一般是调用子类自身的,要调用父类的东西需要加入域限定符
};
void howToCall(A *pA)
{
pA->print();
}
//子类是一中特殊的父类 子类可以当父类使用
//赋值兼容性原则
void Demo01()
{
cout << "-------Demo02--------" << endl;
A a;
B b;
b.b = 3;
b.A::b = 4; //加入域限定符修改父类中的b
a.print();
b.print(); //子类调用父类的print函数,在print函数中输出的b是子类的b
//定义了父类指针
A *pA = NULL;
pA = &b; //pA指向了类的前半截,也就是父类的部分
pA->print(); //因此输出了父类的b
A &myb= b; //引用和指针类似
myb.print();
howToCall(&b); //b会降阶
}
void Demo02()
{
cout << "-------Demo02--------" << endl;
B b1;
b1.add();
b1.print();
b1.A::add(); //加入域限定符调用父类的add函数
b1.print();
}
void main()
{
Demo01();
Demo02();
system("pause");
}
#include <iostream>
using namespace std;
class Object01
{
public:
Object01(const char* s)
{
cout << "Object01()" << " " << s << endl;
}
~Object01()
{
cout << "~Object01()" << endl;
}
};
class Parent02 : public Object01
{
public:
Parent02(const char* s) : Object01(s)
{
cout << "Parent02()" << " " << s << endl;
}
~Parent02()
{
cout << "~Parent02()" << endl;
}
};
class Child03 : public Parent02
{
protected:
Object01 o1;
Object01 o2;
public:
Child03() : o2("o2"), o1("o1"), Parent02("Parameter from Child03!")
{
cout << "Child03()" << endl;
}
~Child03()
{
cout << "~Child03()" << endl;
}
};
void Demo01()
{
Child03 Child;
}
int main(int argc, char *argv[])
{
Demo01();
system("pause");
return 0;
}
其实上面的结果很好解释,考虑到调用子函数是通过栈来调用的就行了。
Demo01函数里构建一个Child03的对象,会调用Child03的构造函数,子类的地址空间是在父类地址变量的后面添加了一部分内容,会先调用父类的构造函数初始化前面的地址空间,然后再按照顺序入栈的顺序初始化后面的变量,调用Object01构造函数先初始化o1,再初始化o2,析构的时候考虑到栈的后进先出特性,首先清理子类的后面的地址空间,因此首先调用子类的构造函数,清理掉子类独有的变量空间,然后调用 Object01系析构掉o2,o1的地址空间,最后让父类清理掉父类独有的地址空间。