书接上回说到,此系列博客为小白学习过c++利用暑假自己在家捣鼓python的一些学习心得体会。
这周来聊一聊面向对象语言的核心(自认为)的类!和类的核心继承,封装,派生(自认为)
6.1.1 c++中的类
,类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。,类和对象的关系就如同数据类型和变量的关系。我们不妨将类看作一种自定义的数据类型,那么对象就是一种属于该类型的变量。其中类包括:为属于该类的全部对象提供了抽象的描述,属性和行为两个主要部分。
6.1.2 c++中类的定义。形式为
一、类定义包括两个部分:
1、说明部分(“做什么”):
数据成员(名称、类型)
成员函数(方法)
2、实现部分(“怎么做”):
成员函数的定义和实现
二、类定义的语法:
class 类名称:
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护型成员
};
三、实现一个钟表类:
#include<iostream>
using namespace std;
class Clock{
public:
void setTime(int newH = 0, int newM = 0, int newS = 0)
{
hour = newH; minute = newM; second = newS;
}
void showTime()
{
cout << hour << ":" << minute << ":" << second << endl;
}
private:
int hour, minute, second;
};
6.1.3 c++中类成员的访问控制
类型 | 权限 |
---|---|
public | 在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数 |
protected | 与private类似,其差别表现在继承与派生时对派生类的影响不同 |
private | 在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。 |
6.1.4 c++中类的一些自带函数
一、c++中的构造函数
class Clock {
public:
Clock(int newH,int newM,int newS);//构造函数
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
//构造函数的实现:
Clock::Clock(int newH,int newM,int newS): hour(newH),minute(newM), second(newS) {
}
//其它函数实现同例4_1
int main() {
Clock c(0,0,0); //自动调用构造函数
c.showTime();
return 0;
}
class Clock {
public:
Clock(int newH, int newM, int newS); //构造函数
Clock(); //默认构造函数
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
Clock::Clock(): hour(0),minute(0),second(0) { }//默认构造函数
//其它函数实现同前
int main() {
Clock c1(0, 0, 0); //调用有参数的构造函数
Clock c2; //调用无参数的构造函数
……
}
c++构造函数注意要点:
函数名与类名相同;
不能定义返回值类型,也不能有return语句;
可以有形式参数,也可以没有形式参数;
可以是内联函数;
可以重载;
可以带默认参数值。
二、c++中的析构函数`
#include <iostream>
using namespace std;
class Point {
public:
Point(int xx,int yy);
~Point();//析构函数
//...其他函数原型
private:
int x, y;
};
Point::Point(int xx,int yy) {
x = xx;
y = yy;
}
Point::~Point() {
}
//...其他函数的实现略
c++析构函数主要要点:
完成对象被删除前的一些清理工作。
在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。
如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数,其函数体为空。
与构造函数一样,析构函数也是类的一个公有成员函数,它的名称是在类名前加“~”构成,没有返回值,和构造函数不同的是析构函数不接受任何参数。
类的析构函数不能重载。
6.1.5 c++中的数据共享与保护
一、c++中的静态数据成员
用关键字static声明
为该类的所有对象共享,静态数据成员具有静态生存期
一般在类外初始化,用(::)来指明所属的类
静态成语的初始化:
<数据类型> <类名>::<static数据成员名 >= <初始化值>;
在初始化语句中不必加static。如果未对静态数据成员赋初值,则编译系统会自动赋予初值0。但是,至少 <数据类型> <类名>::<static数据成员名 >;必须写。
C++11支持静态常量(const或constexpr修饰)类内初始化,此时类外仍可定义该静态成员,但不可再次初始化操作。
#include <iostream>
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0) : x(x), y(y) { //构造函数
//在构造函数中对count累加,所有对象共同维护同一个count
count++;
}
Point(Point &p) { //复制构造函数
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
void showCount() { //输出静态数据成员
cout << " Object count = " << count << endl;
}
private: //私有数据成员
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() { //主函数
Point a(4, 5); //定义对象a,其构造函数回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //输出对象个数
return 0;
}
c++中的静态成员函数
类外代码可以使用类名和作用域操作符来调用静态成员函数。
静态成员函数主要用于处理该类的静态数据成员,可以直接调用静态成员函数。
如果访问非静态成员,要通过对象来访问。
#include <iostream>
using namespace std;
class Point {
public:
Point(int x = 0, int y = 0) : x(x), y(y) { count++; }//构造函数
Point(Point &p) { //复制构造函数
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
static void showCount() {
cout << " Object count = " << count << endl;
}
private:
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
int main() {
Point::showCount(); //输出对象个数
Point a(4, 5); //定义对象a,其构造函数回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
Point::showCount(); //输出对象个数
return 0;
}
6.1.6 c++中类的继承
一、继承提供了在已有类的基础上开发出新类的机制,
可以节省重复代码的编写工作,是软件重用的基础。已知一个类A,对A类加以扩展,即增加一些属性和行为,构成一个新类B,此时B类将A类已有的属性和行为继承下来。称类 B 继承了 A ,或称类 A 派生了 B 。
形式:
A :父类,基类 BaseClass
B :子类,派生类 DerivedClass
单继承时
class 派生类名: 继承方式 基类名
{
成员声明;
}
例如:
class Derived: public Base
{
public:
Derived ();
~Derived ();
};
class employee
{private:
char *name; //姓名
int individualEmpNo; //个人编号
int grade; //级别
float accumPay; //月薪总额
static int employeeNo;//本公司职员编号目前最大值
public:
employee(); //构造函数
~employee(); //析构函数
void pay(); //计算月薪函数
void promote(int); //升级函数
void displayStatus(); //显示人员信息
};
class technician: public employee //兼职技术人员类
{
private:
float hourlyRate; //每小时酬金
int workHours; //当月工作时数
public:
technician(); //构造函数
void pay(); //计算月薪函数
void displayStatus(); //显示人员信息
};
二、类的重写(覆盖)
class deriver: public base
{
public:
void function(){cout<<"function of class deriver"<<endl;}
};
int main()
{ deriver obj;
obj.function();
}
其中:派生类继承了基类的全部数据成员和除了构造、析构函数之外的全部函数成员,但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员,其访问属性由继承方式控制。
继承方式 | 属性 |
---|---|
public | 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问。派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。通过派生类的对象访问从基类继承的成员,只能访问public成员。 |
private | 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。通过派生类的对象不能直接访问从基类继承的任何成员。 |
protected | 基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可直接访问。派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。通过派生类的对象不能直接访问从基类继承的任何成员 |
三、派生类的构造函数
简单的派生类只有一个基类,而且只有一级派生(只有直接派生类,没有间接派生类),在派生类的数据成员中不包含基类的对象。
简单派生类的构造函数的一般形式为:
派生类构造函数名(总参数列表): 基类构造函数名(参数
列表)
{派生类中新增数据成员初始化语句}
#include <iostream>
#include<string>
using namespace std;
class Student //声明基类Student
{public:
Student(int n,string nam,char s) //基类构造函数
{ num=n;
name=nam;
sex=s;
}
~Student( ){ } //基类析构函数
protected: //保护部分
int num;
string name;
char sex ;
};
class Student1: public Student //声明派生类Student1
{public: //派生类的公用部分
Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
//派生类构造函数
{ age=a; //在函数体中只对派生类新增的数据成员初始化
addr=ad;
}
void show( )
{cout<<″num: ″<<num<<endl;
cout<<″name: ″<<name<<endl;
cout<<″sex: ″<<sex<<endl;
cout<<″age: ″<<age<<endl;
cout<<″address: ″<<addr<<endl<<endl;
}
~Student1( ){ } //派生类析构函数
private: //派生类的私有部分
int age;
string addr;
};
int main( )
{Student1 stud1(10010,″Wang-li″,′f′,19,
″115 Beijing Road,Shanghai″);
Student1 stud2(10011,″Zhang-fun″,′m′,21,
″213 Shanghai Road,Beijing″);
stud1.show( ); //输出第一个学生的数据
stud2.show( ); //输出第二个学生的数据
return 0;
}
运行结果为
num:10010
name:Wang-li
sex:f
address: 115 Beijing Road,Shanghai
num:10011
name:Zhang-fun
sex:m
address: 213 Shanghai Road,Beijing
6.6.7 c++中的多态性
函数重载
运算符重载
虚函数
其中又可以分为:
c++中运算符的重载我就不做过多赘述,主要讨论c++中的动态多态
认识c++中的动态多态需要认识虚函数
虚函数是类中的一个用关键字 virtual 修饰的成员函数。
virtual<函数类型><函数名>(形参表)
{ 函数体 }
或者
virtual<函数返回值><函数名>(形参表);
在类的声明中,在函数原型之前写virtual。
virtual 只用来说明类声明中的原型,不能用在函数实现时。
#include <iostream>
using namespace std;
class B0 //基类B0声明
{public: //外部接口
virtual void display() //虚成员函数
{cout<<"B0::display()"<<endl;}
};
class B1: public B0 //公有派生
{ public:
void display() { cout<<"B1::display()"<<endl; }
};
class D1: public B1 //公有派生
{ public:
void display() { cout<<"D1::display()"<<endl; }
};
void fun(B0 *ptr) //普通函数
{ ptr->display(); }
int main() //主函数
{ B0 b0, *p; //声明基类对象和指针
B1 b1,*q; //声明派生类对象
D1 d1; //声明派生类对象
p=&b0; q=&b1;
fun(q);
fun(p); //调用基类B0函数成员
p=&b1;
fun(p); //调用派生类B1函数成员
p=&d1;
fun(p); //调用派生类D1函数成员
}
运行结果
…
写c++部分写了6k多字,我好想确实有点水。
那就好好写写python部分吧
6.2.1 python中的类
创建类时用变量的形式表示对象的特征的成员称为数据成员,用函数的形式表示对象行为的成员称为成员方法,数据成员和成员方法统称为类的成员。需要注意的是,python中的一切对象都可以称为对象,函数是对象,类也是对象。
6.2.2 python中类的定义与使用
python中使用关键词class来定义类,class关键词后是一个空格,然后是类的名字,如果派生自其他基类需要把所有基类放到一对括号中并用逗号隔开,然后是冒号,最后是换行并实现类的内部内容。类的名字一般要大写
胡辣汤点评:可以明显看出来python中类的继承和python有很大不同,不需要说明继承的方式。其他基本和c++差不多
class Car(object):
def info(self):
print('This is a Car')
pass
pass
car = Car()
car.info()
6.2.3 python中类的访问控制
私有成员在类的外部不能直接访问,一般在类的内部进行访问和操作,或者在类的外部通过对象调用公有成员方法来进行访问,而公有成员是可以公开使用的。(这与c++的用法基本一致)
从形式上看,在定义类的成员时,如果成员以两个或者更多下划线开头但不一两个下换线结束(类的内置函数)则表示私有成员,否则不表示私有成员。python对私有成员有严格的保护机制,但通过“对象名._类名__xxx”也可以从外部获取私有成员,但会破坏类的封装性。
class A:
def __init__(self,value = 0,value2 = 0):#构造函数
self._value = value
self.__value2 = value2#value2是私有数据成员
pass
def setValue(self,value1,value2):
self._value = value1
self.__value2 = value2
pass
def showValue(self):
print('_value ={},__value2={}'.format(self._value,self.__value2))
pass
pass
a = A()
print(a._value)
print(a._A__value2)#直接在类外获取私有成员
在python中,以下划线开头或者结束的成员名有特殊含义,在类的定义中用下划线作为成员名的前缀和后缀往往表示特殊的成员
1,_xxx:以一个下划线开头表示保护成员,只有类对象和子类对象可以访问这些成员,在类的外部不建议直接访问。
2,xxx:前后各两个下划线,系统自己定义的特殊成员(如上例中的构造函数)
python相比较c++没有一刀切似的在类中使用public,private,protected来划分不同的成员函数和数据成员,而是类似自助餐似的各取所需
python中的数据成员可以大致分为两类:属于对象的数据成员和属于类的数据成员。属于对象的数据成员一般在构造方法__init__中定义。属于类的数据成员是该类所有对象共享的,不属于任何一个对象。(我认为是类似c++中的静态数据成员)在主程序中或类的外部,对象的数据成员属于对象或者实例,只能通过对象名来访问;而类数据成员属于类,可以通过类名或者对象名来访问
class Demo(object):
total = 0#类数据成员
def __new__(cls, *args, **kwargs):#该方法在构造函数之前调用
if cls.total >=3:
raise Exception('最多只能创建三个对象')
else:
return object.__new__(cls)
pass
def __init__(self):
Demo.total +=1
pass
pass
t1 = Demo()
t2 = Demo()
t3 = Demo()
t4 = Demo()
raise Exception(‘最多只能创建三个对象’)
Exception: 最多只能创建三个对象