c++语法2

上篇我们继续学习静态成员、友元函数、继承、多重继承,继承访问权限等

  • 静态成员与友元函数:
    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 提升或降低继承来的成员访问权限

下一篇将学习多态、类型转换

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • C++文件 例:从文件income. in中读入收入直到文件结束,并将收入和税金输出到文件tax. out。 检查...
    SeanC52111阅读 2,767评论 0 3
  • C++类和对象 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心...
    863cda997e42阅读 642评论 0 4
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,514评论 1 51
  • 1. 让自己习惯C++ 条款01:视C++为一个语言联邦 为了更好的理解C++,我们将C++分解为四个主要次语言:...
    Mr希灵阅读 2,795评论 0 13
  • 有这样一类人,你读书他说你装;你去跑马拉松,他说你有病;你做美美的早餐摆盘拍照,他说要吐。他是你的朋友,十几年熟悉...
    Erin棋落阅读 491评论 2 2