cpp学习记录04:类和对象1

类与对象

面向对象三大特性:封装,继承,多态

封装

例子

//感觉和java差不太多
const int PI = 3.14;

class circle {
    //访问权限
public:
    //属性
    int radius; //半径
    //方法
    double calculateArea() {
        return PI * radius * radius;
    }
protected:
    string name;
};

int main(int argc, char *argv[]) {
    //实例化对象
    circle c1;
    c1.radius = 10;
    cout << "圆的面积是:" << c1.calculateArea() << endl;
}

权限

public:类内可以访问 类外可以访问

protected:类内可以访问 类外不可以访问  子类可以访问

private:类内可以访问 类外不可以访问  子类不可访问

struct和class的区别

唯一区别为默认权限不同

struct默认权限为pulic

class默认权限为private

构造函数和析构函数简介

构造函数:类似java构造器,用于对象初始化

语法:类名(){}

析构函数:用于对象销毁前,执行一些清理工作

语法:~类名(){}

注意点:析构函数不可以有参数,所以也不可以重载

如果不提供构造和析构函数,编译器会提供空实现的构造和析构函数。

class person {
public:
    person() {
        cout << "person()" << endl;
    }
    ~person() {
        cout << "~person()" << endl;
    }
};

void test() {
    person p;
}
int main(int argc, char *argv[]) {
    test();  //在栈上,执行完后,会释放这个对象也就会执行析构函数
}

构造函数

分类

        按参数分:有参构造无参构造

        按类型分:普通构造拷贝构造

class person1 {
public:
    //拷贝构造函数
    person1(const person1 &p) {
        age = p.age;   //将传入对象身上的所有属性,拷贝此对象上
    }
private:
    int age;
};

调用方式:

        括号法

        显示法

        隐式转换法

class person1 {
public:
    person1() {
    }
    person1(int age2) {
        age = age2;
    }
    //拷贝构造函数
    person1(const person1 &p) {
        age = p.age;   //将传入对象身上的所有属性,拷贝此对象上
    }
private:
    int age;
};

void test() {
    //括号法
    //注意调用默认构造函数的时候,不要加()
    //person1 p()    会被认为是函数的声明 ,不会认为在创建对象
    person1 p1(10);
    person1 p2(p1); //调拷贝构造函数

    //显示法
    person1 p3;
    person1 p4 = person1(10);  //等号右侧称为匿名对象
    //匿名对象 特点:当前行执行结束后,系统会立即回收掉匿名对象
    person1 p5 = person1(p4);
    //注意点 不要利用拷贝构造函数 初始化匿名对象
    //编译器会认为person1(p4) === person1 p4 是一个对象的声明
    //person1(p4) 报错p4重定义

    //隐式转换法
    person1 p6 = 10;   //相当于 person1 p6 = person1(10)
    person1 p7 = p6;    //拷贝构造
}
拷贝函数调用时机

1.创建

2.值传递的方式给函数参数传值

void work(person1 p) {
    p.age = 100; //修改这里p的值,不影响原来的对象,
                 //只有创建对象的时候才会调用拷贝构造函数,到这边已经和构造函数无关了
    cout<<"1"<<endl;
}
void test() {
    person1 p;
    work(p); //传值的时候会调用拷贝构造函数
    cout<<p.age<<endl;
}

3.值方式返回局部对象

person1 work1() {
    person1 p1;
    return p1;  //这边返回的不是p1是p1的拷贝
}

深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

浅拷贝会带来的问题

class  Person {
public:
    int age;
    int* height;
    Person() {

    }
    Person(int age1,int height1) {
        age = age1;
        height = new int (height1);
    }
    ~Person() {
        if(height != NULL) {
            delete height;//释放堆区数据
            height = NULL;//防止野指针
        }
    }
};
void test() {
    Person p1(51,190);
    cout<<"p1age"<<p1.age<<"height"<<*p1.height<<endl;
    Person p2(p1);
    cout<<"p2age"<<p2.age<<"height"<<*p2.height<<endl;
}
int main(int argc, char *argv[]) {
    test();
    自带拷贝构造函数是浅拷贝,浅拷贝指针带来堆区内存重复释放
    //Process finished with exit code -1073740940 (0xC0000374)
}

解决:使用深拷贝

//默认拷贝函数为浅拷贝
//自定义拷贝函数该为深拷贝   
 ~Person() {
        if(height != NULL) {
            delete height;//释放堆区数据
            height = NULL;//防止野指针
        }
//添加后能正常结束程序

初始化列表

作用:初始化属性

语法:构造函数():属性1(值1),属性2(值2)...{}

感觉没啥用

class Person {
public:
    int p_a;
    int p_b;
    int p_c;

    //传统初始化操作
    // Person(int a,int b,int c) {
    // p_a = a;
    // p_b = b;
    // p_c = c;
    // }
    //初始化列表初始化属性
    Person(int a,int b,int c):p_a(a),p_b(b),p_c(c) {

    }
};

void test() {
    Person p(10,20,30);
}

类对象作为类成员

构造的时候先构造类对象,再构造自身。析构的顺序与构造的顺序相反

静态成员

就是在成员变量和成员函数前加上关键字static,称为静态成员。

静态成员变量

所有对象共享同一份数据

编译阶段分配内存

类内声明,类外初始化

class Person {
public:
    static int p_a; //类内声明

};
int Person::p_a = 100; //类外初始化操作
静态成员函数

所有对象共享同一个函数

静态成员函数只能访问静态成员变量

class Person {
public:
    static void func() {
        cout<<'1'<<endl;
    }
};
void test() {
    //通过对象访问
    Person p;
    p.func();
    //通过类名访问
    Person::func();
}

this指针

指向被调用的成员函数所属对象

this指针隐含在每一个非静态成员函数内的一种指针,不需要定义,直接使用

用途

当形参和成员变量同名时,可以用this来区分

    Person(int age) {
        this->age = age;
    }

在类的非静态成员函数中返回对象本身,可使用return *this 

class Person {
public:
    int age;
    string name;
    Person(int age) {
        this->age = age;
    }
    //返回本体要用引用的方式  不使用引用的方式即直接用 Person
    //返回的是一个利用拷贝构造函数创建的新的Person对象
    Person& PersonAddAge(Person &p) {
        this->age += p.age;
        return *this; //this是指向本体的指针所以 *this就是本体
    }
};
void test02() {
    Person p(10);
    Person p1(20);
    //链式编程思想
    p1.PersonAddAge(p).PersonAddAge(p);//当返回的对象是本身可以一直追加这个操作
    cout<<p1.age<<endl;
}
.和->的区别

访问结构的成员时使用点运算符,而通过指针访问结构的成员时,则使用箭头运算符

空指针访问成员函数

const修饰成员函数

 常函数

成员函数名后加const后就是常函数,常函数不可以修改成员属性,成员属性声明时加关键字mutable后在常函数中依然可以修改。

class Person {
public:
    void showPerson() const{
        m_B = 100;
        //this 等于是一个指针常量 指针的指向是不可以修改的
        //在成员函数后加this相当于修饰的是this指针 使得指针指向的内容也不可修改了
        //const Person * const this
        //m_A = 100; //这里其实等于 this->m_A = 100;
    }
    int m_A;
    mutable int m_B;  //仍然想修改加mutable
};

常对象

声明对象前加const就是常对象,只能调用常函数

友元

能让一个函数或者类访问另一个类中的私有成员

关键字:friend

全局函数做友元
class Building {
   friend  void GoodFriend(Building *building);
public:
    Building() {
        sittingRoom = "keting";
        bedRoom = "woshi";
    }
    string sittingRoom;
private:
    string bedRoom;
};

void GoodFriend(Building *building) {
    cout<<building->sittingRoom<<endl;
    cout<<building->bedRoom<<endl;
}
类做友元
class Building {
    friend class goodGay;
public:
    Building();
    string sittingRoom;
private:
    string bedRoom;
};
//类外写成员函数
Building::Building() {
    sittingRoom = "keting";
    bedRoom = "woshi";
}

class goodGay {
public:
    Building *b;
    void visit();
    goodGay();
};
goodGay::goodGay() {
    b = new Building;
}
void goodGay::visit() {
    cout<<"fangwen" << b->sittingRoom<<endl;
    cout<<"fangwen"<<b->bedRoom<<endl;
}
void test2() {
    goodGay g;
    g.visit();
}
成员函数做友元
class Building;
class goodGay {
public:
    Building *b;
    void visit();
    void visit2();
    goodGay();
};
class Building {
    friend void goodGay::visit();
public:
    Building();
    string sittingRoom;
private:
    string bedRoom;
};
//类外写成员函数
goodGay::goodGay() {
    b = new Building;
}
void goodGay::visit() {
    cout<<"fangwen" << b->sittingRoom<<endl;
    cout<<"fangwen"<<b->bedRoom<<endl;
}
void goodGay::visit2() {
    cout<<"fangwen" << b->sittingRoom<<endl;
    //cout<<"fangwen"<<b->bedRoom<<endl;
}
Building::Building() {
    sittingRoom = "keting";
    bedRoom = "woshi";
}
void test2() {
    goodGay g;
    g.visit();
}

运算符重载

对已有运算符重新定义,以适应不同数据类型

加法运算符重载

法1:成员函数重载

class Person {
public:
    int a;
    int b;
    Person(){}
    Person(int a,int b){
    this->a = a;
    this->b = b;
    }
    //成员函数重载+号
    Person operator+(Person &p) {
        Person temp;
        temp.a = this->a + p.a;
        temp.b = this->b + p.b;
        return temp;
    }
};

void test() {
    Person a = Person(5,6);
    Person b = Person(7,8);
    //成员函数重载本质 Person c = a.operator+(b);
    Person c = a + b ;
}

法2:全局函数重载

class Person {
public:
    int a;
    int b;
    Person(){}
    Person(int a,int b){
    this->a = a;
    this->b = b;
    }
};
// 全局函数重载+号
 Person operator+(Person &p1,Person &p2) {
     Person temp;
     temp.a = p1.a +p2.a;
     temp.b = p1.b + p2.b;
     return temp;
 }
void test() {
    Person a = Person(5,6);
    Person b = Person(7,8);
     //全局函数重载本质 Person c = operator+(a,b)
    Person c = a + b ;
}

左移运算符重载

用来输出对象

不会用成员函数重载<<运算符,因为无法实现cout在左侧

ostream& operator<<(ostream &cout,Person &p) {
 cout<<"p.a = "<<p.a<<"p.b = "<<p.b;
     return cout;   //还是链式编程的思想
 }
void test() {
    Person a = Person(5,6);
     cout<<a<<endl;   //不反回cout的话后面没法继续输出
}
递增运算符重载
class MyInterge {
  friend ostream& operator<<(ostream &cout,const MyInterge &myint);
 public:
     //重载前置++运算符
     MyInterge& operator++() { //返回引用是为一直对同一个数据进行操作
         ++m_Num;               //换句话来说,没有引用返回的就是新的对象
         return *this;
     }
     //重载后置++运算符
     //void operator++(int) 这里的int是一个占位参数,用来区分前置和后置递增
     MyInterge operator++(int)  //只能是int
     {//后置++运算符要返回值 因为这里的temp是局部变量,函数结束后就释放掉了
        //先  记录当时结果
         MyInterge temp = *this;
         //后  递增
         ++m_Num;
         //最后将记录结果返回
         return temp;
     }

     MyInterge() {
         m_Num = 0;
     }
     private:
     int m_Num;
 };

注意这里后置递增是不满足链式法的

赋值运算符重载

用来避免浅拷贝带来的问题

class Per {
public:
    int* age;
    Per& operator=(Per &p) {
        //编译器提供的是浅拷贝
        //age = p.age;

        //先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
        if(age!=NULL) {
            delete age;
            age = NULL;
        }
        age = new int(*p.age);//深拷贝
        return *this;
    }

    Per(int a) {
        age = new int(a);
    }
    ~Per() {
        if(age!=NULL) {
            delete age;
            age =NULL;
        }
    }
};

void test03() {
    Per p1 = 17;
    Per p2 = 19;
    p1 = p2;
}

关系运算符(==,!=,等)的重载和上述类似,就不多说了

函数调用运算符的重载

函数调用运算符重载后的使用方式非常像函数的调用,因此称为仿函数。

再STL中常用这里先不细说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值