美文网首页C++
3.类和对象(封装)

3.类和对象(封装)

作者: 徐凯_xp | 来源:发表于2018-12-10 00:42 被阅读70次
封装

类的基本思想:数据抽象和封装
数据抽象是一种依赖接口和实现分离的编程技术
接口:类的用户所能执行的操作
实现:类的数据成员、接口函数的实现及其他私有函数的实现
封装:实现了类的接口和实现分离
封装后的类隐藏了实现细节;
类的用户只能使用接口而无法访问实现部分。
面向对象三大特性:封装、继承、多态

访问控制

struct结构体→结构体变量
class类→对象

  1. 数据与行为不分离(成员变量,成员函数)
  2. 权限控制:
    类内开放,类外控制
    保证数据的完整、正确性
  3. 成员函数,调用内部变量不需要传参
    访问说明符:
    public:公共的
    protected:保护的
    private:私有的
    C++中class和struct没有本质区别,只是默认权不同。
C语言版
///c格式,struct
#include <stdio.h>
typedef struct _Date {
    int y, m, d;
}Date;
int isValid(Date *pd) {
    if (pd->y > 0 && pd->y < 9999 &&
        pd->m >0 && pd->m < 13 && 
        pd->d>0 && pd->d < 32)
        return 1;
    return 0;
}
void init(Date *pd) {
    while (1) {
        printf("请输入年 月 日:\n");
        scanf("%d%d%d", &pd->y, &pd->m, &pd->d);
        if (isValid(pd)) break;
        printf("输入格式错误,重新输入!\n");
    }
}
void print(Date *pd) {
    printf("%d-%d-%d\n", pd->y, pd->m, pd->d);
}
int main()
{
    Date d1;
    init(&d1);
    print(&d1);
    return 0;
}

C语言的问题:结构体中所有东西都是可见和操作的

C++版
#include <iostream>
using namespace std;
class Date {
public:
    void init() {
        while (1) {
            cout << "请输入年 月 日:" << endl;
            cin >> y >> m >> d;
            if (isValid()) break;
            cout << "输入错误,重新输入!" << endl;
        }
    }
    void print() {
        cout << y << "-" << m << "-" << d << endl;
    }
private:
    bool isValid() {
        if (y > 0 && y < 9999 && m>0 &&
            m < 13 && d>0 && d < 32)
            return true;
        return false;
    }
    int y, m, d;
};
int main() {
    Date d1;
    d1.init();
    d1.print();
    return 0;
}
栈类
#include <iostream>
using namespace std;
class Stack {
public:
    bool isEmpty()const { return topidx == 0; }
    bool isFull()const { return topidx == size; }
    void init(int len = 1024) {
        ps = new int[len];
        size = len;
        topidx = 0;
    }
    void destroy() {
        if (ps) delete[] ps;
        ps = NULL;
    }
    int top()const {
        return ps[topidx - 1];
    }
    void push(int data) {
        ps[topidx++] = data;
    }
    void pop() {
        topidx--;
    }
private:
    int *ps;
    int topidx;
    int size;
};
int main() {
    Stack S;
    S.init();
    for (int i = 0; i < 5; i++) {
        if (!S.isFull())
            S.push(i);
    }
    while (!S.isEmpty()) {
        cout << S.top() << " ";
        S.pop();
    }
    //S.topidx = 10; //错误,无法修改
    S.destroy();
    return 0;
}

构造函数与析构函数

构造函数:每个类都定义了它的对象被初始化的方式,类通过一个或多个特殊的成员函数来控制其对象的初始化。(生成对象时自动调用)
析构函数:释放对象使用的资源。(对象销毁时自动调用)

构造函数:与类名相同,无返回,可以有参数
析构函数:~与类名相同,无参数,无返回

#include <iostream>
using namespace std;
class Stack {
public:
    Stack(int len = 1024) {
        ps = new int[len];
        size = len;
        topidx = 0;
        cout << "构造函数run..." << endl;
    }
    ~Stack() {
        if (ps) delete[] ps;
        ps = NULL;
        cout << "析构函数run..." << endl;
    }
    bool isEmpty()const { return topidx == 0; }
    bool isFull()const { return topidx == size; }
    int top()const {
        return ps[topidx - 1];
    }
    void push(int data) {
        ps[topidx++] = data;
    }
    void pop() {
        topidx--;
    }
private:
    int *ps;
    int topidx;
    int size;
};
int main() {
    Stack S;
    //S.init(); //不再需要,被构造函数替代了
    for (int i = 0; i < 5; i++) {
        if (!S.isFull())
            S.push(i);
    }
    while (!S.isEmpty()) {
        cout << S.top() << " ";
        S.pop();
    }
    cout << endl;
    //S.destroy(); //不再需要,被析构函数替代了
    return 0;
}
构造函数初步
  1. 可以有参数,有默认参数,可以重载
  2. 若未提供构造参数,系统默认生成一个无参空构造函数;若提供,则不再默认生成无参空构造函数

类名a; //调用无参构造函数[不能写成类名a(),编译器会认为是函数声明]
类名a();//调用有参构造函数,a{xx}也可以
通过new在堆空间创建对象,同样会自动调用构造函数

#include <iostream>
using namespace std;
class Stack {
public:
    Stack() { //无参构造函数
        ps = new int[1024];
        size = 1024;
        topidx = 0;
        cout << "Stack() run" << endl;
    }
    Stack(int len) { //带参构造函数
        ps = new int[len];
        size = len;
        topidx = 0;
        cout << "Stack(int len) run" << endl;
    }

    /*Stack(int len = 1024) {
    ps = new int[len];
    size = len;
    topidx = 0;
    }*/
    ~Stack() {
        if (ps) delete[] ps;
        ps = NULL;
        cout << "析构函数run..." << endl;
    }
    bool isEmpty()const { return topidx == 0; }
    bool isFull()const { return topidx == size; }
    int top()const {
        return ps[topidx - 1];
    }
    void push(int data) {
        ps[topidx++] = data;
    }
    void pop() {
        topidx--;
    }
private:
    int *ps;
    int topidx;
    int size;
};
int main() {
    Stack S1;      //调用无参构造 不能写 Stack S1();
    Stack S2(100); //调用带参构造
    Stack S3{ 10 };//调用带参构造
    Stack *p1 = new Stack; //无参构造
    Stack *p2 = new Stack(10);//带参构造
    Stack *p3 = new Stack{ 10 };//带参构造
                                //对照:
    int a1; //不能写 int a1(); 这是函数声明
    int a2(10);
    int a3{ 10 };
    int *pa1 = new int;
    int *pa2 = new int(10);
    int *pa3 = new int{ 20 };
    return 0;
}
mystring类的构造函数

模范标准库的string类:
string s1; // 无参构造函数
string s2("abc");//有参构造函数

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
class mystring{
public:
    mystring(){
        ps = new char[1];
        ps[0] = '\0';
    }
    mystring(const char *str){
        int len = strlen(str) + 1;
        ps = new char[len];
        strcpy(ps,str);
    }
        /*
    myString(const char *str = NULL) {
    if (str == NULL) {
    ps = new char[1];
    ps[0] = '\0';
    }
    else {
    int len = strlen(str) + 1;
    ps = new char[len];
    strcpy(ps, str);
    }
    }*/
    ~mystring(){
        delete[] ps;
    }
    const char* c_str() const{
        return ps;
    }
private:
    char *ps;
};
int main(){
    string s1;
    string s2("abc");
    cout<<"-"<<s1.c_str()<<"-"<<endl;
    cout<<s2.c_str()<<endl;
    mystring ms1;
    mystring ms2("abc");
   cout<<"-"<<ms1.c_str()<<"-"<<endl;
    cout<<ms2.c_str()<<endl;
}

析构函数初步

析构函数:释放对象使用的资源(对象销毁时自动调用)
1.无参,无返回(不可重载)
2.若为提供,系统默认生成一个空的析构函数
通过delete销毁堆空间上的对象,同样会自动调用析构。
设计原则:自己申请的资源,自己负责释放

///析构函数初步 c代码
struct Stu {
    char *name;
    int age;
};
int main() {
    //申请st1的内存
    struct Stu *st1 = 
       (struct Stu*)malloc(sizeof(struct Stu));
    //申请st1中name的内存
    st1->name = (char*)malloc(sizeof(char)*20);
    //释放st1中name的内存
    free(st1->name);
    //释放st1本身的内存
    free(st1);
    return 0;
}

C代码要逐级分别释放各自申请的资源

///C++代码
class Stu {
public:
    Stu() {
        name = new char[20]; 
        age = 10; 
    }
    ~Stu() {
        if (name) delete[] name; 
    }
private:
    char *name;
    int age;
};
int main() {
    //申请st1的内存
    Stu *st1 = new Stu;
    delete st1;
    return 0;
}
构造与析构的次序

1.多个对象,按次序构造,析构次序相反
2.类中有成员变量也是类对象的时候,先运行成员类的构造函数,在运行本类的构造函数。析构次序与构造次序相反。
3.注意类中成员变量是类的指针类型的话,不会调用构造函数

#include <iostream>
#include <stdio.h>
using namespace std;
class A {
public:
    A(int i = 0) {
        num = i;
        cout << "A()" << num << endl;
    }
    ~A() { cout << "~A()" << num << endl;   }
private:
    int num;
};
class B {
public:
    B() { cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }
};
int main() {
    A a1;
    A a2(1);
    B b;
    A *pa = new A[2]{ 2,3 }; //连续构造2次
    delete[] pa;             //连续析构2次
    return 0;
}
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
class myString {
public:
    myString(const char *str = NULL) {
        if (str == NULL) {
            ps = new char[1];
            ps[0] = '\0';
        }
        else {
            int len = strlen(str) + 1;
            ps = new char[len];
            strcpy(ps, str);
        }
        cout << "myString构造" << endl;
    }
    ~myString() {
        if (ps) delete[] ps;
        cout << "myString析构" << endl;
    }
private:
    char *ps;
};
class Stu {
public:
    Stu() { cout << "Stu构造" << endl;    }
    ~Stu() { cout << "Stu析构" << endl; } 
private:
    myString name;
    int age;
};
int main() {
    Stu st1;
    return 0;
}
#include <iostream>
using namespace std;
class A {
public:
    A() { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }
};
void fun() { A a2; }
int main() {
    {
        A a1;
    }
    //到这里,a1已经析构了。
    fun();
    //到这里,fun中的a2已经析构了。
    cout << "----------" << endl;
    int i = 0;
    //注意a3的作用域
    for (A a3; i < 3; i++) {
        cout << i << endl;
    }
    cout << "----------" << endl;
    //注意a4的作用域
    for (int j = 0; j < 3; j++) {
        A a4; //每次循环都会构造
        cout << j << endl;
    }//while循环也是如此
    cout << "----------" << endl;
    A *pa = new A; //构造
    delete pa; //调用delete析构
    return 0;
}
  1. 栈空间中的对象脱离作用域时,析构
  2. 堆空间中的对象delete时,析构
类文件写法

通常将一个类分为2个文件:类的声明写在类名.h;类的实现写在类名.cpp
一个类就是一个作用域
在类的外部定义成员函数时,返回值类型 类名::函数名(参数)

///myString.h
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
class myString {
public:
    myString(const char *str = NULL);
    ~myString();
    const char* c_str()const;
private:
    char *ps;
};
#endif // !MYSTRING_H


///调用
#include <iostream>
#include "myString.h"
using namespace std;

int main() {
    myString s1;
    return 0;
}
///myString.cpp
#include <string.h>
#include "myString.h"
//注意,默认参数要写在函数声明中
myString::myString(const char *str) {
    if (str == NULL) {
        ps = new char[1];
        ps[0] = '\0';
    }
    else {
        int len = strlen(str) + 1;
        ps = new char[len];
        strcpy(ps, str);
    }
}
myString::~myString() {
    delete[] ps;
}
const char* myString::c_str()const {
    return ps;
}
对象的内存

类→对象,模具→产品
创建对象时:只有成员变量开辟内存,没有成员变量是,占用一个字节。成员函数并不占用对象的空间。
class 中的成员变量和C中的struct一样,要对齐、补齐。

#include <iostream>
using namespace std;
class A1 {};
class A2 { void fun() {} };
class A3 { int num; void fun() {} };
class A4 { int num; char name[6]; };
int main() {
    cout << sizeof(A1) << endl; //1
    cout << sizeof(A2) << endl; //1
    cout << sizeof(A3) << endl; //4
    cout << sizeof(A4) << endl; //12
    return 0;
}
#include <iostream>
#include <string.h>
using namespace std;

class myString {
public:
    myString(const char *str = NULL);
    ~myString();
private:
    char *ps; int size;
};
myString::myString(const char *str) {
    if (str == NULL) {
        ps = new char[1];
        ps[0] = '\0';
    }
    else {
        int len = strlen(str) + 1;
        ps = new char[len];
        strcpy(ps, str);
    }
}
myString::~myString() {
    delete[] ps;
    cout << "~myString()" << endl;
}

class Person {
public:
    Person() { age = 20; p_fm = NULL; }
    void set_fm(){
        p_fm = new myString[2];
    }
    ~Person() { 
        cout << "~Person()" << endl;
        delete[] p_fm; 
        cout << "=========" << endl;
    }
private:
    int age;        //年龄
    myString name;  //自己的名字
    myString *p_fm; //父母的名字
};

int main() {
    Person p1;
    p1.set_fm();
    return 0;
}

在内存怎么存储:
person P1 在栈里面:


this 指针

This 指针可以认为是顶层const,不能修改
为了区分num,对于外层的num用A::num表示,当然也可以用this->num

#include <iostream>
#include <string.h>
using namespace std;
class A {
public:
    A(int num) { A::num = num; }
    A(double num) { this->num = num; }
    int num; 
};
int main() {
    A a1(20);
    cout << a1.num << endl; //20
    A a2(1.2);
    cout << a2.num << endl; //1
    return 0;
}

对象的内存和函数没有关系,怎么区分是哪一个num?
show函数是如何知道返回的是a1还是a2的num?
可想像为:int A::show( A* const this);
调用a1.show(&a1); a2.show(&a2);
成员函数隐式地传递了一个当前对象的指针参数
this指针:指向的是本对象的地址

#include <iostream>
#include <string.h>
using namespace std;
class A {
public:
    A(int num) { this->num = num; }
    int show()const { return num; }
private:
    int num; 
};
int main() {
    A a1(20);
    cout << a1.show() << endl; //20
    A a2(1);
    cout << a2.show() << endl; //1
    return 0;
}

构造函数初始值列表

有些类中,初始化和赋值的区别事关低层效率的问题,初始化的先后顺序和成员变量出现的先后次序一致

#include <iostream>
#include <string.h>
using namespace std;
class A {
public:
    A(int n, double f) { num = n; fd = f; }
private:
    int num;
    double fd;
};
class B {
public:
                  //构造函数的初始值列表
    B(int n, double f) :num(n), fd(f) {}
private:
    int num;
    double fd;
};
int main() {
    A a(10, 1.2);
    B b(10, 1.2);
    return 0;
}

A类在创建对象时,先分别对num和fd调用默认初始化;
然后再通过赋值语句给num和fd赋值。
B类在创建对象时,直接初始化num和fd.
A的流程:int num ; num = 10 ;
B的流程: int num(10);
假如是类成员变量:A--> myString ma; ms = "abc";
B--> myString ms("abc");

#include <iostream>
#include <string.h>
using namespace std;
class B {
public:
    B(double f) :fd(f), num(fd) {}
    int get_num()const { return num; }
private:
    int num;
    double fd;
};
int main() {
    B b(1.2);
    cout << b.get_num() << endl;
    //没有得到预期中的 1
    return 0;
}

初始值列表对成员变量的初始化先后次序,与成员变量在类中出现的先后次序一致。
尝试用排序靠后的成员变量来初始化前面的成员变量,会得到未知的结果。

拷贝构造和赋值运算符重载

拷贝构造函数:
class 类名{
类名(const 类名 & another);
}

  1. 系统提供默认的拷贝构造,若自己提供,则不复存在。
  2. 默认拷贝构造是等位拷贝,也就是所谓的浅拷贝。
  3. 要实现深拷贝,必须要自己实现

赋值运算符重载:
类名{
类名& operator = (const 类名 & 源对象)
....;
return *this;
}与拷贝构造类似

string s1("abc");  // 直接初始化
string s2("1234");
string s3(s2);    //  直接初始化
string s4 = s2;   // 拷贝初始化
string s5;
s5 = s2;         // 赋值操作
默认的拷贝构造(浅拷贝)
class A {
public:
    A(int n = 0) : num(n){}
    A(const A &other) :num(other.num){}
private:
    int num;
}
int main(){
    A a1;
    A a2(a1);    //直接初始化
    A a3 = a1; // 拷贝初始化
    const A a4;
    A a5 = a4; //参数没有const,就错误
    return 0;
}
运算符重载
#include <iostream>
#include <string>
using namespace std;

class A {
public:
    A(int n = 0) :num(n) {}
    A(const A &other) :num(other.num){  }
    A& operator=(const A &other) {
        num = other.num;
        return *this;
    }
private:
    int num; 
};
int main() {
    A a1;
    A a2;
    a2 = a1; //赋值
    return 0;
}
浅拷贝

浅拷贝可能会造成内存泄漏、重析构

myString(const char * str = NULL){
    ps = new char[len];
}
~myString(){
    if(ps)
        delete[] ps;
}

拷贝构造:myString s2 = s1;
等位拷贝:s1的ps指针赋给s2的ps


s1.ps 和 s2.ps同时指向同一个内存
s1,s2分别析构时,会两次释放同一内存

赋值运算符重载:

myString s1('AB')
myString s2('CD')
s2 = s1;

存在内存泄漏和重析构

深拷贝实现
class myString{
public:
    myString(const char *str = NULL){
        if(str == NULL){
            ps = new char[1];
            ps[0] = '\0';
        }
        else{
            int len = strlen(str) +1;
            ps = new char[len];
            strcpy(ps,str);
        }
    }
}
myString(cosnt myString &other)'
myString& operator= (const myString & other);
~myString(){
    delete[] ps;
 }
private:
    char *ps
拷贝构造
myString :: myString(const myString &other){
    ps = new char[srelen(other.ps) +1];
    strcpy(ps,other.ps);//拷贝构造
}
赋值运算符重载
myString & myString::operator = (const myString &other){
    if(this == &other)//排除自己给自己赋值出错
        return *this;
    delete[] ps;
    ps = new char[strlen(other.ps) + 1];
    strcpy(ps, other.ps);
    return *this;
}
友元

封装:类的数据成员定义为私有成员,外部要访问类的私有成员只能通过接口(如,get和set方法)
特殊情况:类外有些函数需要频繁的访问类的数据成员,此时可以将这些函数定义为该类的友元函数。友元的好处:提高了程序的运行效率(减少类型和安全检查及调用的时间开销)
友元的副作用:破坏了类的封装性和隐藏性,使得非成员函数可以直接访问类的私有成员。
友元
类的接口实现功能:

#include <iostream>
using namespace std;
class Person {
public:
    void set_m_p_piece(int m) {
        if (m >= 10.0 && m <= 100.0)
            m_p_piece = m;
        else
            m_p_piece = 10.0;
    }
    double get_m_p_piece()const {
        return m_p_piece;
    }
    void set_t_piece(int t) {
        if (t <= 0) t_piece = 0;
        else t_piece = t;
    }
    int get_t_piece()const {
        return t_piece;
    }
private:
    double m_p_piece; //单件工资
    int t_piece;      //总件数
};

void set_Person(Person& person, double m, int t) {
    person.set_m_p_piece(m);
    person.set_t_piece(t);
}
double calc_Person(const Person& person) {
    return 
        person.get_m_p_piece()*person.get_t_piece();
}
int main() {
    Person person1;
    for (int i = 0; i < 100000; i++) {
        set_Person(person1, i, i);
        cout << calc_Person(person1) << endl;
    }
    return 0;
}

友元函数:

#include <iostream>
using namespace std;
class Person {
public:
    friend void set_Person(Person& person, double m, int t);
    friend double calc_Person(const Person& person);
    void set_m_p_piece(int m) {
        if (m >= 10.0 && m <= 100.0)
            m_p_piece = m;
        else m_p_piece = 10.0;
    }
    double get_m_p_piece()const { return m_p_piece; }
    void set_t_piece(int t) {
        if (t <= 0) t_piece = 0;
        else t_piece = t;
    }
    int get_t_piece()const { return t_piece; }
private:
    double m_p_piece; //单件工资
    int t_piece;      //总件数
};

void set_Person(Person& person, double m, int t) {
    person.m_p_piece = m;
    person.t_piece = t;
}
double calc_Person(const Person& person) {
    return  person.m_p_piece * person.t_piece;
}
int main() {
    Person person1;
    for (int i = 0; i < 100000; i++) {
        set_Person(person1, i, i);
        cout << calc_Person(person1) << endl;
    }
    return 0;
}
前向声明
#include <iostream>
#include <cmath>
using namespace std;

class Point;        //前向声明
class ManagerPoint{ //管理Point的类
public:
    double distance(Point &a, Point &b);
};

class Point{        //Point类
public:
    friend double ManagerPoint::distance(const Point &a, const Point &b);
    Point(double x, double y):x(x),y(y) { }
    void print()const;
    const double &get_x()const { return x; }
    const double &get_y()const { return y; }
private:
    double x, y;
};

void Point::print()const {
    cout << "(" << x << "," << y << ")" << endl;
}

double ManagerPoint::distance(const Point &a,const  Point &b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx*dx + dy*dy);
}

int main(){
    Point p1(1.0, 1.0), p2(4.0, 5.0);
    p1.print();
    p2.print();
    ManagerPoint mp;
    double d = mp.distance(p1, p2);
    cout << "Distance = " << d << endl;
    return 0;
}

前向声明:是一种不完全声明

  1. 不能定义类的对象
    2.可以用于定义指向这个类类型的指针或引用
  2. 用于声明,使用该类类型作为形参类型或者返回值类型
友元
  1. 友元关系不能被继承
  2. 友元关系是单向的,不具有交换性。若B是A的友元,A不一定是B的友元
  3. 友元关系不具有传递性。
    若B是A的友元(B可以访问A的私有成员)
    C是B的友元(C可以访问B的私有成员)
    不等于C是A的友元
#include <iostream>
using namespace std;

class Point {
public:
    friend class Stack; //友元类
    Point(double x = 0.0, double y = 0.0) 
        :x(x), y(y) {}
private:
    double x, y;
};

class Stack {
public:
    Stack(int len = 1024);
    ~Stack();
    bool isEmpty()const;
    bool isFull()const; 
    const Point &top()const;
    void push(const Point& data) {
        ps[topidx++] = data;
        cout << "(" << data.x << "," 
            << data.y << ")" << endl;
    }
    void pop();
private:
    Point *ps;
    int topidx;
    int size;
};
inline bool Stack::isFull()const {
    return topidx == size;
}

Stack::Stack(int len) : topidx(0),
size(len) {
    ps = new Point[len];
}
Stack::~Stack() {
    delete[] ps;
    ps = NULL;
}
bool Stack::isEmpty()const {
    return topidx == 0;
}
const Point& Stack::top()const {
    return ps[topidx - 1];
}

void Stack::push(const Point &data) {
    ps[topidx++] = data;
    cout << "(" << data.x << "," << data.y << ")" << endl;
}

void Stack::pop() {
    topidx--;
}
int main(){
    Point p1(1, 1);
    Stack S;
    S.push(p1);
    return 0;
}
类的const常量成员

const成员变量,不能修改,只能在初始值列表中初始化

class A{
public :
    A(int i = 0) : ci(i){}
    //const 成员变量,只能通过初始值列表初始化
    // A(int i = 0){ci = i;}//错误!
    // void set_ci(int i ){ci = i;}//错误
private:
    const int ci ;
}

const 成员函数:

  1. const放在函数声明之后,实现体之前 void fun() const{}
  2. 承诺在本函数内部不会修改类内的数据成员,不会调用其它非const成员函数
  3. const 构成函数重载(why?this指针底层const)
#include <iostream>
using namespace std;
class A {
public:
    A(int i = 0) :ci(i),num(i) {  }
    int get_ci()const {
        return ci;
    }
    void show()const {
        //set_num(0); //错误,调用非const函数
        //num = 0; //错误,修改数据成员
        cout << num*ci << endl; //正确,调用不修改
        get_ci(); //正确,调用const函数
        cout << "show() const" << endl;
    }
    void show() { //重载
        cout << "show()" << endl;
    }
    int set_num(int i) { num = i; }
private:
    const int ci;
    int num;
};

int main(){
    A a; //普通对象
    const A ca; //const对象
    //ca.set_num(100); //错误,const对象只能调用const函数
    ca.show(); // show() const
    a.show(); //show() 非const [非const对象也可调用const函数]
    return 0;
}

类的static静态成员

static成员变量:同类对象间信息共享类外存储,必须类外初始化,可通过类名访问,也可通过对象访问。
静态成员函数:管理静态数据成员,对静态数据成员封装(对外提供接口) (可以认为和全局函数相似)
静态成员函数:只能访问静态数据成员。静态成员函数属于类,没有this指针

#include <iostream>
using namespace std;
class Person {
public:
    Person(int _age) :age(_age) { sum += _age; count++; }
    static int get_count();
    static double get_avg() { return double(sum) / count; }
private:
    int age;         //年龄
    static int count;//总人数
public:
    //不能 static int sum=0;
    static int sum;  //年龄总和
};

//此处不能加 static
int Person::get_count() { return count; }
//此处不能加 static
int Person::sum = 0;   //必须初始化
int Person::count = 0; //数据区(bss,rw?)

int main(){
    //外部直接访问方式,类名::静态变量名
    cout << Person::sum << endl; 
    //cout << Person::count; //错误,同样有权限控制
    //静态成员变量不占用对象的内存空间
    cout << sizeof(Person) << endl; //4

    Person p1(10);
    //静态变量,也可以通过 对象.变量名 来访问
    //类外需要权限,类内部都行
    cout << p1.sum << endl;
    Person p2(20);

    //静态成员函数,可以通过 类名::函数名 来访问
    cout << Person::get_avg() << endl;
    //也可以通过 对象.函数名 来访问
    cout << p1.get_count() << endl; //2
    cout << p2.get_count() << endl; //与上面输出一致
    return 0;
}
单例模式
  1. 将默认的构造函数和析构函数声明为私有,外部无法创建,无法销毁(只能自己销毁自己)
  2. 使用一个私有的静态本类类型的指针变量,用来指向该类的唯一实例
  3. 用一个公有的静态方法来获取该实例,第一次调用该方法时,创建实例并返回(懒汉式),以后调用直接返回
  4. 用一个公有的静态方法来删除该实例,以保证该实例只会被删除一次。
#include <iostream>
using namespace std;
class A {
private:
    A() {}; //构造和析构私有
    ~A() {};
public:
    static A * GetInstance() {//公有静态方法,可以获取该唯一实例
        if (NULL == m_pInstance) m_pInstance = new A; //(多线程需要加锁)
        return m_pInstance;
    }
    static void DeleteInstance() {//公有静态方法,可以删除该实例
        if (m_pInstance != NULL)
            delete m_pInstance;
        m_pInstance = NULL;
    }
private:
    static A *m_pInstance; //私有静态指针变量:指向类的唯一实例
    int count; //其他成员变量...
};

A *A::m_pInstance = NULL;    //懒汉式
//A *A::m_pInstance = new A; //饿汉式

int main() {
    //A a; //错误,外部无法创建该类对象
    A *pa = A::GetInstance(); //通过调用 类静态成员函数 来获取类对象
    A *pb = A::GetInstance(); //可多次调用
    cout << pa << pb << endl; //地址相同,pa,pb指向的是同一个对象
    //delete pa; //错误,外部无法直接销毁该类对象
    A::DeleteInstance(); //通过调用 类静态成员函数 来析构类对象
    return 0;
}

myList

练习1

实现一个链表类:

  1. 带头结点
  2. 数据类型是Student类,学号起始编号是100
  3. 实现下列功能:
    myList(const myList& other);//深拷贝构造
    mylist &operator=(const myList &other);//赋值重载
    void insert_tail(int_age, const string&_name);//尾插
    bool del_node(int_id);//按id删除节点
    void print() const;//遍历打印
    inline int get_size() cosnt;//获取链表中元素个数
class myList{
private:
    Student_node *head;
    Student_node *tail;
    int  size;
};

class Student_node{
pravate:
    int id;
    int age;
    std::strting name;
    Student_node *next;
}
Student_node类(结点):
  1. 学号自动增长。static静态变量和静态函数
  2. Student_node类没有实现析构、拷贝构造、赋值运算符重载,可行吗?
  3. 假如学员姓名用自己写的String 类,那么要在哪里自己实现上面的那些函数?
  4. 声明了myList是Student_Node 的友元类,假如不允许使用友元,该类还需要提供那些函数?
class Student_node{
    friend class myList;
public:
    Student_Node(bool flag = flase, int _age = 20, const std::string & _name = “ ”):id(calc_id(flag)),age(_age), name(_name), next(NULL){}
    static int calc_id(bool flag){
        if(flag)
            return  now_id ++;
        return 0;
    }
pravate:
    int id;
    int age;
    std::strting name;
    Student_node *next;
private:
    static int now_id ;
}

相关文章

  • 3.类和对象(封装)

    封装 类的基本思想:数据抽象和封装数据抽象是一种依赖接口和实现分离的编程技术接口:类的用户所能执行的操作实现:类的...

  • 面向对象编程基础

    面向对象思想有三大要素: 1.封装2.继承3.多态 类和对象 所谓的类便是对象的蓝图,对象是类的实例。类就像将某些...

  • Python学习笔记——类和对象

    类和对象 1. 一个例子 2. 面向对象的特征 ① 封装 ② 继承 ③ 多态 3. self 4. _ _ in...

  • 面向对象的三大特性

    封装 继承 多态 封装 封装是面向对象的特征之一,是对象和类概念的主要特性。也就是把客观事物封装成抽象的类,并且类...

  • Java_类和对象

    面向对象的三大特点:封装、继承、多态封装:封装是面向对象的核心思想。将对象的属性和行为封装起来。其载体就是类,类通...

  • 面对对象程序(二)

    面向对象三大特性: 1. 封装 封装是面向对象编程的核心思想。封装的载体是类,对象的属性和行为被封装在这个类中。例...

  • 面向对象的三大特性小结

    封装 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,...

  • 面相对象的特征

    封装 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,...

  • P 面向对象 | 面向对象的封装特性

    一、封装 将属性和方法封装到一个抽象的类中外界使用类创建对象,然后让对象调用方法对象方法的细节都被封装在类的内部 ...

  • Scala入门与进阶(四)- Scala面向对象

    Scala面向对象 1.面向对象概述 封装:属性方法封装到类中 继承:父类和子类直接的关系 多态:***** 父类...

网友评论

    本文标题:3.类和对象(封装)

    本文链接:https://www.haomeiwen.com/subject/djylqqtx.html