接上篇我们继续学习静态成员、友元函数、继承、多重继承,继承访问权限等
- 静态成员与友元函数:
c++中静态成员与java中的类似,他们都属于类的,并且只有一份。例如我们在Person中定义一个静态cnt来统计一共创建过多少个person,这个cnt属于整个类的,但是类中只是声明,还需要在类外定义。使用时提供访问静态方法getCount(),Person::getCount();
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
static int cnt;
char *name;
int age;
char *work;
public:
static int getCount(void) {
return cnt;
}
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
cnt++;
}
Person(char *name) {
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
cnt++;
}
Person(char *name, int age, char *work = "none") {
cout <<"Pserson(char*, int), name = "<<name<<", age= "<<age<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
cnt++;
}
Person(Person &per) {
cout <<"Pserson(Person &)"<<endl;
this->age = per.age;
this->name = new char[strlen(per.name) + 1];
strcpy(this->name, per.name);
this->work = new char[strlen(per.work) + 1];
strcpy(this->work, per.work);
cnt++;
}
~Person(){
cout << "~Person()"<<endl;
if (this->name) {
cout << "name = "<<name<<endl;
delete this->name;
}
if (this->work) {
cout << "work = "<<work<<endl;
delete this->work;
}
}
void setName(char *n){
name = n;
}
int setAge(int a){
if (a < 0 || a > 150){
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void){
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int Person::cnt = 0; /* 定义和初始化 */
int main(int argc, char **argv){
cout << "person number = "<<Person::getCount()<<endl;
return 0;
}
友元:友元函数是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。
友元关系不具对称性。即 A 是 B 的友元,但 B 不一定是 A 的友元。 友元关系不具传递性。即 B 是 A 的友元,C 是 B 的友元,但是 C 不一定是 A 的友元。
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point() {}
Point(int x, int y) : x(x), y(y) {}
int getX(){ return x; }
int getY(){ return y; }
void setX(int x){ this->x = x; }
void setY(int y){ this->y = y; }
void printInfo(){
cout<<"("<<x<<", "<<y<<")"<<endl;
}
friend Point add(Point &p1, Point &p2);
};
Point add(Point &p1, Point &p2){
Point n;
n.x = p1.x+p2.x;
n.y = p1.y+p2.y;
return n;
}
int main(int argc, char **argv){
Point p1(1, 2);
Point p2(2, 3);
p1.getX();
p2.getX();
Point sum = add(p1, p2);
sum.printInfo();
return 0;
}
说明:在该程序中的Point类中说明了一个友元函数add(),它在说明时前边加friend关键字,标识它不是成员函数,而是友元函数。它的定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。但是,它可以引用类中的私有成员,函数体中p1.x,p2.x,p1.y,p2.y都是类的私有成员,它们是通过对象引用的。在调用友元函数时,也是同普通函数的调用一样,不要像成员函数那样调用。本例中,p1.getX()和p2.getX()这是成员函数的调用,要用对象来表示。而add(p1, p2)是友元函数的调用,它直接调用,不需要对象表示,它的参数是对象。
友元的正确使用能减少方法的调用,提高程序的运行效率,但同时也破坏了类的封装性和数据的隐藏性,导致程序可维护性变差。
- 继承:
通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员
在C++语言中,一个派生类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承。
单继承的定义格式如下:
class<派生类名>:<继承方式><基类名>
{
<派生类新定义成员>
};
先写一个Student继承Person
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
public:
int address;
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
}
Person(char *name) {
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
Person(char *name, int age) {
cout <<"Pserson(char*, int), name = "<<name<<", age= "<<age<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
Person(Person &per) {
cout <<"Pserson(Person &)"<<endl;
this->age = per.age;
this->name = new char[strlen(per.name) + 1];
strcpy(this->name, per.name);
}
~Person(){
cout << "~Person()"<<endl;
if (this->name) {
cout << "name = "<<name<<endl;
delete this->name;
}
}
void setName(char *name){
if (this->name) {
delete this->name;
}
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
int setAge(int a){
if (a < 0 || a > 150){
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void){
cout<<"name = "<<name<<", age = "<<age<<endl;
}
};
class Student : public Person {
private:
int grade;
void setGrade(int grade) {this->grade = grade;}
int getGrade(void) {return grade;}
public:
void printInfo(void){
cout<<"Student ";
Person::printInfo();
}
};
void test_func(Person &p){
p.printInfo();
}
int main(int argc, char **argv){
Person p("lisi", 16);
Student s;
s.setName("zhangsan");
s.setAge(16);
test_func(p);
test_func(s); /* Person &p = s里面的Person部分;
* p引用的是"s里面的Person部分"
*/
s.printInfo();
return 0;
}
定义一个接收Person引用的test_func函数,当我们调用test_func(s)时,将student的引用传入,只会使用Person部分,而不会去调用student的printInfo函数。
输出结果:
Pserson(char*, int), name = lisi, age= 16
//test_func(p)执行
name = lisi, age = 16
//test_func(s)执行
name = zhangsan, age = 16
//s.printInfo();执行
Student name = zhangsan, age = 16
~Person()
name = zhangsan
~Person()
name = lisi
多继承的定义格式如下:
class<派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…{
<派生类新定义成员>
};
定义一个沙发床,有两个基类,沙发,床,沙发可以看电视,床可以睡觉。
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Sofa {
public:
void watchTV(void) { cout<<"watch TV"<<endl; }
};
class Bed {
public:
void sleep(void) { cout<<"sleep"<<endl; }
};
class Sofabed : public Sofa, public Bed {
};
int main(int argc, char **argv){
Sofabed s;
s.watchTV();
s.sleep();
return 0;
}
输出:
watch TV
sleep
可以看出沙发床自身没有看电视,睡觉的功能,而是从基类里面继承而来。但是如果多个基类里面有相同的成员怎么办呢?例如沙发中有重量weight,床也有重量weight,这就是多继承会引发的二义性。
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Sofa {
private:
int weight;
public:
void watchTV(void) { cout<<"watch TV"<<endl; }
void setWeight(int weight) { this->weight = weight; }
int getWeight(void) const { return weight; }
};
class Bed {
private:
int weight;
public:
void sleep(void) { cout<<"sleep"<<endl; }
void setWeight(int weight) { this->weight = weight; }
int getWeight(void) const { return weight; }
};
class Sofabed : public Sofa, public Bed {
};
int main(int argc, char **argv){
Sofabed s;
s.watchTV();
s.sleep();
//s.setWeight(100); /* 这样调用会有错误,有二义性*/
s.Sofa::setWeight(100);/* 可以指定这个方法属于哪个基类 */
return 0;
}
那该怎么解决?C++中使用虚拟继承解决。将相同的成员提取出来,得到虚拟基类。例如我们这里可以抽象出虚拟基类家具Furniture,它里面有weight重量,沙发与床虚拟继承家具,这样沙发与床共用家具成员weight。从而解决二义性
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Furniture {
private:
int weight;
public:
void setWeight(int weight) { this->weight = weight; }
int getWeight(void) const { return weight; }
};
//这里是虚拟继承
class Sofa : virtual public Furniture {
private:
int a;
public:
void watchTV(void) { cout<<"watch TV"<<endl; }
};
class Bed : virtual public Furniture {
private:
int b;
public:
void sleep(void) { cout<<"sleep"<<endl; }
};
class Sofabed : public Sofa, public Bed {
private:
int c;
};
int main(int argc, char **argv){
Sofabed s;
s.watchTV();
s.sleep();
// 这里就使用虚拟基类中的weight,从而避免了二义性
s.setWeight(100);
return 0;
}
1. 公有继承(public)
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。
2. 私有继承(private)
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
3. 保护继承(protected)
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
下面列出三种不同的继承方式的基类特性和派生类特性。
public | protected | private | |
---|---|---|---|
公有继承 | public | protected | 不可见 |
保护继承 | protected | protected | 不可见 |
私有继承 | private | private | 不可见 |
- 模拟场景:儿子可以向父亲要钱,但是不能直接拿
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Father {
private:
int money;
protected:
int room_key;
public:
void it_skill(void){
cout<<"father's it skill"<<endl;
}
int getMoney(void){
return money;
}
void setMoney(int money){
this->money = money;
}
};
class Son : public Father {
private:
int toy;
// 可以降低访问权限
//using Father::it_skill;
public:
//可以提升成员的访问权限
using Father::room_key;
//using Father::money;
void play_game(void){
int m;
cout<<"son paly game"<<endl;
/* money -= 1;
* 错: 不能直接拿父亲的私房钱
*/
/*
* 但是可以问他要
*/
m = getMoney();
m--;
setMoney(m);
//但是可以拿家里的钥匙
room_key = 1;
}
/* 覆写 override */
void it_skill(void){
cout<<"son's it skill"<<endl;
}
};
int main(int argc, char **argv){
Son s;
s.setMoney(10);
cout << s.getMoney()<<endl;
s.it_skill();
s.play_game();
s.room_key = 1;
return 0;
}
父亲的私有属性儿子不能直接访问,但是受保护的成员儿子是可以访问的。儿子可以通过using 提升或降低继承来的成员访问权限
下一篇将学习多态、类型转换
网友评论