C++第一弹面向对象之构造函数和析构函数

228 阅读7分钟

1、C与C++的区别

C++面向对象+标准特性
C 面向过程 函数+结构体
C++ 里可以运行C语言, 可以调用C语言, 反之就不行。C语言无法运行C++ 以后写NDK85%以上用C++去写功能

Cpp代码HelloWorl

 #include <iostream> //C++标准库
using namespace std;//命名空间,有点类似于(Java语言里的内部类)

int main() {

   // std::cout << "Hello, World!" << std::endl;
    // << 不是属性里面的运算,操作符重载, Kotlin里也有
 //Kotlin 抄go的协程, 抄了C++的操作符重载
   cout << "Hello, World!" << endl;//因为引入了命名空间,所以可以省略std
   cout << "换行\n"
           <<" ddddd"
    <<"ccccccc"<<endl;
    return 0;
}

2、C的常量和C++的常量

C语言的常量其实是个假常量。

image.png

image.png 但是在C++中这个问题已经被解决,C++里的常量是个真常量

image.png

3、引用的原理和常量引用

引用的原理
我们在C语言中,在方法中传递参数的时候,形参是值传递。

 #include <iostream> //C++标准库
using namespace std;//命名空间,有点类似于(Java语言里的内部类)
void referenceTest(int number1,int number2) {
    cout << "referenceTest 方法 number1的地址:"
   << &number1
   <<"number2的地址:"
   << &number2 << endl;//这里打印的地址和main方法打印的地址肯定是不一样的

}
int main () {
    int number1 = 100;
    int number2 = 200;
    cout << "main 方法 number1的地址:"
    << &number1
    <<"number2的地址:"
    << &number2 << endl;
    referenceTest(number1,number2);
}

但是在C++里有个,引用传递

 #include <iostream> //C++标准库
using namespace std;//命名空间,有点类似于(Java语言里的内部类)
void referenceTest(int & number1,int & number2) {
    cout << "referenceTest 方法 number1的地址:"
   << &number1
   <<"number2的地址:"
   << &number2 << endl;

}
int main () {
    int number1 = 100;
    int number2 = 200;
    cout << "main 方法 number1的地址:"
    << &number1
    <<"number2的地址:"
    << &number2 << endl;
    referenceTest(number1,number2);
}

image.png 这次打印的地址是一样的,引用传递。

int number = 100;
int number1 = number;
这两句代码在内存中是这样的

image.png int number =100;
int & number1 = number;
这两句代码在内存中是这样的

1722932459090.jpg 如果你去修改这三个值里面的任何一个,其他两个的值都会改变的,因为三个引用指向了同一个地址。
常量引用
常量引用是不可以修改的,其他的和引用是一样的。

void referenceTest2 (const Student & student) {
    //student 只读
    strcpy(student.name,"大明白");//会报错 不能修改
    Student student2 =student;// 会报错不能修改
}

4、函数重载和默认形参

C是不支持函数重载的,C++支持了函数重载,和Java一样!允许两个方法名相同,参数不同的方法

void test(int number1,int number2) {
   
}
void test (int number1) {//没什么可说的
   
}

默认形参
形参可以设置默认值,在调用的时候,如果不传则是默认值这个在Kotlin里也有这个特性

void test (int n1 =0 ,int n2=100, int n3=200) {
    cout << n1 <<" "<< n2 << " " <<n3<<endl;
}

//void test(int number1){这个方法放开之后,编辑器会懵逼
//
//}
int main () {
    test(1);

    return  0;
}

但是如果有四个参数一下的函数重载,编辑器就会懵逼,就会报错,编辑器不知道你要调那个方法,因为test(1),符合默认形参的调用,也符合一个参数的函数的调用,它就不知道要调用哪个,就会报一个错误给你Ambiguous function call.模棱两可的方法被调用

但是在C++中还有一种神奇的写法

void test (char * log ,int ,int ) {
    
}

这样写是不报错的,没有形参别名,在很多框架和系统源码里都是这样写的,其解释是,在开发第一版的时候,为了以后得扩展性,后边两个参数是定义好了,但是在第一版并没有用到,在第二版的时候才开始写真正的功能。但是在调用的时候,你必须传递后边两个参数,就是我可以不用,你不可以不传。

1722997343951.jpg

5、C++的实体类写法

C++的类写法比Java有点麻烦,着实有点麻烦,在头文件里声明类名,属性和方法名,cpp文件里写实现。 头文件,

#include <iostream>

using namespace std;

class Student {
private: //一个private可以修饰多个成员属性 ,int和age都是私有的
    char *name;
    int age;

public:
    void setName(char *name); //在头文件里,只写声明,不写实现
    void setAge(int age);

    char *getName();
    int getAge();


};

实现文件

#include "Student.h"

//这样写,和头文件的方法没有半毛钱关系,,,
void setAge(int age) {

}
//这样写才可以 -> 一级指针访问成员变量
void Student::setAge(int age) {
    this->age =age;
}
void Student::setName(char *name) {
    this->name = name;
}
int Student::getAge() {
    return age;

}
char *Student::getName() {

    return name;
}

类的使用,需要注意的是,在栈区和堆区开辟是完全不同的用法,堆区用到了new 关键字,是不是很熟悉哈哈哈

int main () {
    //栈空间开辟 的对象
    Student student;
    student.setName((char*)"夏洛");
    student.setAge(18);
    cout << student.getName()<<" "<<student.getAge()<<endl;
    //堆空间开辟 的对象
    Student * student1 = new Student();
    student1->setName((char*)"马冬梅");
    student1->setAge(18);
    cout << student1->getName()<<" "<<student1->getAge()<<endl;
    delete student1; //释放堆空间内存
    student1 = NULL;
    return  0;
}

6、构造函数和析构函数

C++里的构造函数,和Java里的差不多,析构函数有所区别,可能写Android,和Java的同学,没听说过析构函数的概念。析构函数有点像Activity里的onDestory函数,类似对象的生命周期吧 在C++中析构函数执行的时间,是在对象在delete 的时候执行,可以在析构函数里执行一些内存回收动作(这里一会再讲)

//无参构造
Student::Student() {
    cout << "无参构造"<< endl;
}
//一个参数的构造 如果调用 一个参数的构造,它会先执行无参构造方法,再执行 一个参数构造方法里面的代码
Student::Student(char *name) :Student(){
    //this(); java 的写法
    cout << "一个参数的构造"<< endl;
    this->name =name;//java里的写法
}
//两个参数的构造
//给参数赋值 :name(name),age(age) C++写法
Student::Student(char *name, int age) :name(name),age(age) {
    cout << "两个参数的构造"<< endl;
}
//析构函数。析构函数是不能有参数的。有了参数编辑器会懵逼的,编译不会通过的
Student::~Student() {
    cout << "析构函数"<< endl;
}

我们在调用一个参数的构造方法时,它会限制性无参构造,再执行调用的构造方法,给参数赋值C++的写法也是很有逼格。现在我们去调用一番。

#include "Student.h"
int main () {
    //栈空间开辟 的对象
    Student student;//这句话执行完,就已经调用了 无参构造
    Student student2((char *)"大傻春");//执行了一个参数的构造
    cout << student2.getName()<<" "<<student2.getAge()<<endl;
    //堆空间开辟 的对象
    Student * student1 = new Student();
    student1->setName((char*)"马冬梅");
    student1->setAge(18);
    cout << student1->getName()<<" "<<student1->getAge()<<endl;
    delete student1; //调用析构函数
    student1 = NULL;
    

    return  0;
}//执行结束出栈,student 和student 2 被回收,回收之前执行析构函数

image.png 析构函数的的执行时机,我们已经掌握清楚了,但是我们在析构函数里都干点啥啊? 在C语言里,我们都用malloc 去申请内存,free 去释放内存,我能这样写么?

Student * student3 =(Student *) malloc(sizeof(Student));
//对student进行操作
free(student3);

这样是不行的,这样是不会执行Student的构造方法的,这两句代码只是在申请了Student 大小的内存,和释放了内存,Student并没有执行任何操作。我们也不能用free 去替代 delete,否则析构函数也不会执行的。如果非得这样干,会被C++工程师鄙视,吊起来打的。。

image.png malloc 和free要成对出现new 和 delete 要成对出现

7、总结

介绍了类的写法,和类的使用,还有类里面比较重要的几个函数构造函数和析构函数。其中还有拷贝函数,拷贝函数内容太多,拷贝函数,深拷贝浅拷贝原理,我们就放到第二弹去写。
牢记在栈内初始化的和在堆内初始化的用法不同不要被同行吊起来打。
希望大家一键三连,多多支持!