C++学习二

2021-03-17  本文已影响0人  芳心之纵火犯

一、构造函数和析构函数


#include <iostream>
#include <string.h>
using namespace std;

class Student {

// 构造函数
public:
    // 空参数构造函数
    Student() {
        cout << "空参数构造函数" << endl;
    }

    // 一个参数的构造函数
    //Student(char *name){
    //    cout << "一个参数的构造函数" << endl;
    //    this->name = name;
    //}
    
    // :Student(name, 87) 等价 1.调用两个参数的构造函数, 2.再调用当前函数
    // 先调用两个参数的,再调用一个的
    Student(char *name) :Student(name, 87) {
        cout << "一个参数的构造函数" << endl;
        this->name = name;
    }
    // 系统源码中是写的
    // :name(name) 等价 this->name = name;
    /*Student(char * name) :name(name) {
        cout << "一个参数的构造函数" << endl;
    }*/

    // 两个参数的构造函数
    Student(char *name, int age) {
        // this->name = name;

        // 堆区
        this->name = (char *) (malloc(sizeof(char *) * 10));
        strcpy(this->name, name);

        this->age = age;
        cout << "两个参数的构造函数" << endl;
    }

    // 析构函数 Student对象的,临终遗言,Student对象被回收了,你做一些释放工作
    // delete stu 的时候,我们的析构函数一定执行
    // free不会执行析构函数,也意味着,你没法在析构函数里面,做释放工作, malloc也不会调用构造函数
    ~Student() {
        cout << "析构函数" << endl;

        // 必须释放 堆区开辟的成员
        if (this->name) {
            free(this->name);
            this->name = NULL; // 执行NULL的地址,避免出现悬空指针
        }
    }

// 私有属性
private:
    char *name;
    int age;

// 公开的 set get 函数
public:
    int getAge() {
        return this->age;
    }

    char *getName() {
        return this->name;
    }

    void setAge(int age) {
        this->age = age;
    }

    void setName(char *name) {
        this->name = name;
    }
};

int main() {
    // TODO  =========== 下面是栈区 开辟空间的

    /*Student stu; // 调用 空参数构造函数
    stu.setAge(34);
    stu.setName("李元霸");
    cout << "name:" << stu.getName() << ", age:" << stu.getAge() <<  endl;*/

    // Student stu("雄霸", 30);
    /*Student stu("李连杰");
    cout << "name:" << stu.getName() << ", age:" << stu.getAge() <<  endl;*/

    // TODO =========== 下面是堆区 开辟空间的  堆区必须手动释放,否则内存占用越来

    // *stu  ->:调用一级指针的成员
    Student *stu = new Student("杜子腾", 26);
    cout << "name:" << stu->getName() << ", age:" << stu->getAge() <<  endl;
    delete stu;

    // malloc 你的构造函数都没有调用,这个不行的
    /*Student *stu2 = (Student*) malloc(sizeof(Student));
    free(stu2);*/

    return 0;
}

二、拷贝构造函数

struct Person {
    int age;
    char *name;
};
// TODO = 号的意义 隐士代码,引出 拷贝构造函数
/*
int main() {
    Person person1 = {100, "张三丰"};
    // = 你看起来,没有什么特殊,隐士的代码:你看不到  C/C++编译器 会把p1的成员值赋值给p2成员
    Person person2 = person1;
    //地址不一样
    cout << &person1 << endl; 
    cout << &person2 << endl;

    cout << person2.name << ",  " << person2.age << endl;

    // 对象 对象1=对象2  默认的 拷贝构造函数 如图1

    return 0;
}*/
拷贝构造函数01.png
#include <iostream>
#include <string.h>
using namespace std;
class Student {
public:
    Student() { cout << "空参数构造函数" << endl; }

    // 两个参数的构造函数
    Student(char *name, int age) : name(name), age(age) {
        cout << "两个参数构造函数" << endl;
    }
    // 析构函数:不能有参数
    // ~Student(char * name) { } 
    ~Student() {
        cout << "析构函数" << endl;
    }

    // 拷贝构造函数,它默认有,我们看不到,一旦我们写拷贝构造函数,会覆盖她
    // 对象1 = 对象2
    // 覆盖拷贝构造函数
    Student(const Student & student) { // 常量引用:只读的,不让你修改
        cout << "拷贝构造函数" << endl;

        // 我们自己赋值
        // 为什么要自己赋值,自己来控制,就可以 例如:-10
        this->name = student.name;
        this->age = student.age - 10;
        cout << "自定义拷贝构造函数 内存地址 " << &student << endl;
    }

// 私有属性
private:
    char *name;
    int age;

// 公开的 set get 函数
public:
    int getAge() {
        return this->age;
    }
    char *getName() {
        return this->name;
    }
    void setAge(int age) {
        this->age = age;
    }
    void setName(char *name) {
        this->name = name;
    }
};
int main() {
    Student stu1("李鬼", 34);
    Student stu2 = stu1;

    cout << stu2.getName() << " , " <<  stu2.getAge() <<  endl;
    cout << "main " << &stu1 << endl; // 地址的打印是一样的,  注意:cnetos的环境 地址打印有差异,要注意

    // TODO  拷贝构造函数的注意点:
    // Student stu1("李鬼", 34);

    // Student stu2;
    // stu2 = stu1; // 这样赋值是不会调用 自定义拷贝构造函数,但是会调用默认赋值
    // Student stu2 = stu1;  // 这样赋值是会调用 自定义拷贝构造函数,我们自己赋值
    // cout << stu2.getName() << " , " <<  stu2.getAge() <<  endl;
    getchar(); // 程序等待在这一行
    return 0;
} // main函数弹,stu1栈成员会回收  stu2栈成员会回收
*/

// TODO 这种写法 拷贝构造函数  到底会不会调用
int main() {
    Student *student1 = new Student("杜子腾", 39);

    Student *student2 = student1;  // 压根就不会执行拷贝构造函数(指针指向问题,和我们刚刚那个  对象2=对象1 是两回事) 如图2  
    student2->setAge(99);
    cout << student1->getName() << student1->getAge() << endl; //99 
    return 0;
}
拷贝构造函数02.png

三、常量相关

#include <iostream>
#include <string.h>
#include <string.h>
using namespace std;
int main() {
    // *strcpy (char *__restrict, const char *__restrict);
    // strcpy()

    int number = 9;
    int number2 = 8;

    // 常量指针
    const int * numberP1 = &number;
    // *numberP1 = 100; // 报错,不允许去修改【常量指针】存放地址所对应的值
    // numberP1 = &number2; // OK,允许重新指向【常量指针】存放的地址

    //  指针常量
    int* const numberP2 = &number;
    *numberP2 = 100; // OK,允许去修改【指针常量】存放地址所对应的值
    // numberP2 = &number2; // 报错,不允许重新指向【指针常量】存放的地址

    // 常量指针常量
    const int * const numberP3 = &number;
    // *numberP3 = 100; // 报错,不允许去修改【常量指针常量】存放地址所对应的值
    // numberP3 = &number2; // 报错,不允许重新指向【常量指针常量】存放的地址

    return 0;
}

四、static关键字

/**
 * 静态的总结:
 * 1.可以直接通过类名::静态成员(字段/函数)
 * 2.静态的属性必须要初始化,然后再实现(规则)
 * 3.静态的函数只能取操作静态的属性和方法(Java)
 */
#include <iostream>
using namespace std;
class Dog {
public:
    char * info;
    int age;

    // 先声明
    static int id;
    static void update() {
        id += 100;
        // 报错:静态函数不能调用非静态函数(Java)
        // update2();
    }
    void update2() {
        id = 13;
    }
};

// 再实现
int Dog::id = 9;

int main() {
    Dog dog;
    dog.update2(); // 普通函数
    Dog::update(); // 静态函数
    dog.update(); // 对象名.静态函数(一般都是使用::调用静态成员,这种方式可以 知道就行)
    cout << Dog::id << endl;
    return 0;
}

五、this

#include <iostream>
using namespace std;
class Worker {
// 默认的构造函数 栈区开辟空间 暴露 地址 == this指针
public:
    char * name;
    int age = NULL; // C++中不像Java,Java有默认值, 如果你不给默认值,那么就是系统值 -64664

    // int * const  指针常量【地址对应的值能改,地址不可以修改】
    // const int *  常量指针【地址可以修改,地址对应的值不能改】

    // 纠结:原理:为什么可以修改age
    // 默认持有隐式的this【类型 * const this】
    void change1() {
        // 代表指针地址不能被修改
        // this = 0x6546;  // 编译不通过,地址不能被修改,因为是指针常量

        // 隐式的this
        // 但是指针地址的值是可以修改的
        // 地址对应的值能改
        this->age = 100;
        this->name = "JJJ";
    }
    // 默认现在:this 等价于 const Student * const  常量指针常量(地址不能改,地址对应的值不能改)
    void changeAction() const {
        // 地址不能改
        // this = 0x43563;
        // 地址对应的值不能改
        // this->age = 100;
    }

    // 原理:修改隐士代码  const 类型 * const 常量指针常量
    void showInfo() const {
        // this->name = "";
        // this->age = 88;
        // 只读的
        cout << "age:" << age << endl;
    }
};

int main() {
    return 0;
}

六、友元函数

#include <iostream>
using namespace std;
class Person {
private: // 私有的age,外界不能访问
    int age = 0;
public:
    Person(int age) {
        this->age = age;
    }
    int getAge() {
        return this->age;
    }
    // 定义友元函数 (声明,没有实现)
    friend void updateAge(Person * person, int age);
};

// 友元函数的实现,可以访问所以私有成员
void updateAge(Person* person, int age) {
    // 默认情况下:不能修改 私有的age
    // 谁有这个权限:友元(拿到所有私有成员)
    person->age = age;
}
int main() {
    Person person = Person(9);
    updateAge(&person, 88);
    cout << person.getAge() << endl;
    return 0;
}

七、友元类

// ImageView 私有成员  可以通过Class来访问,但是Class操作的native C++代码)
#include <iostream>
using namespace std;
class ImageView {
private:
    int viewSize;
    friend class Class; // 友元类
};

// Java每个类,都会有一个Class,此Class可以操作 ImageView私有成员
class Class {
public:
    ImageView imageView;
    void changeViewSize(int size) {
        imageView.viewSize = size;
    }
    int getViewSize() {
        return imageView.viewSize;
    }
};
int main() {
    Class mImageViewClass;
    mImageViewClass.changeViewSize(600);
    cout << mImageViewClass.getViewSize() << endl;
    return 0;
}

八、总结

//Pig.h文件
#include <iostream>
using namespace std;
#ifndef PIG_H // 你有没有这个宏(Java 宏==常量)
#define PIG_H // 定义这个宏
class Pig {
private:
    int age;
    char * name;

public:
    // 静态成员声明
    static int id;

    // 构造函数的声明系列
    Pig();
    Pig(char *);
    Pig(char *,int);

    // 析构函数
    ~Pig();

    // 拷贝构造函数
    Pig(const Pig & pig);

    // 普通函数 set get
    int getAge();
    char * getName();
    void setAge(int);
    void setName(char *);

    void showPigInfo() const; // 常量指针常量 只读

    // 静态函数的声明
    static void changeTag(int age);

    // 友元函数的声明
    friend void changeAge(Pig * pig, int age);
};

#endif // 关闭/结尾
//Pig.cpp
#include "Pig.h"

// TODO  ========下面是 普普通通 常规操作 对象::

// 实现构造函数
Pig::Pig() {
    cout << "默认构造函数" << endl;
}

Pig::Pig(char * name) {
    cout << "1个参数构造函数" << endl;
}

Pig::Pig(char * name, int age) {
    cout << "2个参数构造函数" << endl;
}

// 实现析构函数
Pig::~Pig() {
    cout << "析构函数" << endl;
}

// 实现 拷贝构造函数
Pig::Pig(const Pig &pig) {
    cout << "拷贝构造函数" << endl;
}

int Pig::getAge() {
    return this->age;
}
char * Pig::getName() {
    return this->name;
}
void Pig::setAge(int age) {
    this->age = age;
}
void Pig::setName(char * name) {
    this->name = name;
}

void Pig::showPigInfo() const {

} // 常量指针常量 只读

// TODO ===================  静态 和 友元 注意点 自己理解

// 实现 静态属性【不需要增加 static关键字】
int Pig::id = 878;

// 实现静态函数,【不需要增加 static关键字】
void Pig::changeTag(int age) {

}

// 友元的实现
// 友元特殊:不需要关键字,也不需要 对象:: ,只需要保证 函数名(参数)
void changeAge(Pig * pig, int age) {

}
上一篇 下一篇

猜你喜欢

热点阅读