NDK开发之C++语言学习第三节课内容回顾笔记
一、介绍了C++的命名空间概率.
C++可以用namespace关键字自定义命名空间
//自定义命名空间
namespace lin {
int age;
char *name;
void show() {
//打印 C++中的打印方式
cout << "age = "<< age << " name = "<< name << endl;
}
void action(){
//打印 C++中的打印方式
cout << " lin action" << endl;
}
}
然后可以通过using namespace lin; 声明命名空间, 然后可以通过两种方式来使用命名空间内的属性和方法.
第一种
int main() {
using namespace lin ;
lin::age = 18;
lin::name = "林俊杰";
lin::show();
lin::action();
return 0;
}
打印结果如下:
image.png
第二种:
int main() {
using namespace lin ;
age = 18;
name = "林俊杰";
show();
action();
return 0;
}
打印结果如下:
image.png
两个结果是一样的 都可以使用命名空间的属性和函数,但是第二种方面有个缺点就是 如果命名空间里面有重复的函数和属性时,就不能通过第二种方式.
在自定义一个命名空间 lin2,里面在定义一个跟lin一样的属性age和函数action
namespace lin2 {
int age;
void action(){
//打印 C++中的打印方式
cout << " Lin2 action" << endl;
}
}
然后再main方法中声明using namespace lin2,并通过第二种方式使用定义的age和action;
image.png
我们可以看到直接编译不通过,提示我们调用的action方法不明确,因为有两个action 编译器不知道使用的是哪一个. 所以在有重复的属性和函数的时候使用第一种方法,没有的时候使用第二种,毕竟第二种使用方便.
C++的命名空间是可以嵌套的就是可以在命名空间里面在嵌套一个命名空间.
namespace lin3{
namespace Inner1{
namespace Inner2{
namespace Inner3{
void out(){
//打印 C++中的打印方式
cout << "这是命名空间嵌套" << endl;
}
}
}
}
}
使用的时候 还是先声明 using namespace lin3::Inner1::Inner2::Inner3;
int main() {
using namespace lin3::Inner1::Inner2::Inner3;
out(); //第二种方式使用
lin3::Inner1::Inner2::Inner3::out(); //第一种方式使用
return 0;
}
打印结果如下:
image.png
二、C++的构造函数, C++里面也是有构造函数的, 结构类型跟java很像
C++中的构造函数也是用关键字class修饰的 ,里面也是有公有属性和私有属性, 空参构造和有参构造
class Student { //构造函数
public: //公有属性
//空参构造
Student() {
//打印 C++中的打印方式
cout << "空参构造函数" << endl;
}
//一个参数的构造函数
Student(char *name) {
this->name = name;
//打印 C++中的打印方式
cout << "一个参数构造函数 name = " << this->name << endl;
}
//两个参数的构造函数
Student(char *name,int age) {
this->name = name;
this->age = age;
//打印 C++中的打印方式
cout << "两个个参数构造函数 name = " << this->name << " age = "<< this->age << endl;
}
private: //私有属性
char *name;
int age;
};
定义好了 构造函数,那如果使用呢
int main() {
Student stu; //调用空参构造
Student stud("林俊杰"); //调用一个参数的构造
Student student("林俊杰",30); //调用两个参数的构造
return 0;
}
我们来看看打印结果 看看是不是调用相应的构造函数
image.png
可以看到都调用了相应的构造函数. 我们看到因为name和age是私有属性,不能直接访问,如果我们想通过空参构造函数去设置name和age怎么办呢,其实跟java一样 可以在公有属性设置set方法和get方法来.
//空参构造
Student() {
cout << "空参构造函数" << endl;
}
void setAge(int age) {
this->age = age;
}
int getAge() {
return this->age;
}
void setName(char *name) {
this->name = name;
}
char *getName() {
return this->name;
}
然后通过调用set和get方法对属性进行赋值和取值;
Student *s = new Student(); //调用空参构造
s->setName("周杰伦");
s->setAge(35);
cout << "name = " << s->getName() << " age = "<< s->getAge()<< endl;
打印结果
image.png
在C++中也是栈区和堆区的概念,跟java比较像. 像上面一种方式就是在栈区开辟空间的 在main函数出栈后里面的资源都会被释放.
下面讲下通过堆区开辟空间
int main() {
// TODO =========== 下面是栈区 开辟空间的
Student stu; //调用空参构造
Student stud("林俊杰"); //调用一个参数的构造
Student student("林俊杰",30); //调用两个参数的构造
// TODO =========== 下面是堆区 开辟空间的
Student *s = new Student(); //调用空参构造
Student *stu1 = new Student("杜子腾"); //调用一个参数的构造
Student *stu2 = new Student("杜子腾", 26); //调用两个参数的构造
return 0;
}
堆区开辟空间是不是很java很像 都是通过new的方式.但是通过堆区开辟空间是要我们手动释放的,不然内存占用的会越来越多. 所以每次new完都要用delete关键字释放占用的内存.new和delete是成对出现的.
Student *s = new Student(); //调用空参构造
delete s;
Student *stu1 = new Student("杜子腾"); //调用一个参数的构造
delete stu1;
Student *stu2 = new Student("杜子腾", 26); //调用两个参数的构造
delete stu2;
在这里扩展一下,有参构造有时候还有另外一种写法,你可能会在一些源码中看到这样的写法
//:name(name) 等价 this->name = name;
Student(char *name) : name(name) {
cout << "一个参数的构造函数" << endl;
}
在一些系统源码里面会有很多这样的写法 :name(name)等价this->name = name;
我们再来看下另外一种有意思写法:
Student(char *name) : Student(name, 87) {
this->name = name;
cout << "一个参数构造函数 name = " << this->name << endl;
}
我们在main方法是创建下构造函数 Student *stu1 = new Student("杜子腾");, 大家来猜一猜结果是怎么样的
image.png
打印结果是先调用了两个参数的构造函数,在调用的一个参数的构造函数.至于为什么会这样我也不是很清楚.
三、C++的析构函数
在学习析构函数之前我们先来了解下什么是析构函数,析构函数是用来做什么用的。
析构函数可以说构造函数Student对象的临终遗言,在Student对象被回收后,帮你做一些释放工作.
析构函数是用 ~符号修饰的 如下所示
//析构函数 Student对象的临终遗言,Student对象被回收了,你做一些释放工作
~Student() {
cout << "析构函数" << endl;
}
我们在执行delete student对象的时候一定会调用析构函数 ,我们来在main方法里delete student'对象来试试析构函数是否会执行
image.png
我们可以看到执行了析构函数. 具体的用法就是我们在构造函数里面在堆区开辟空间,我delete student的时候就必须把在堆区开辟的空间释放,这时候用析构函数来释放占用内存. 例如下面
我们在两个参数的构造函数里面 在堆区开辟一个空间在析构函数中释放
// 两个参数的构造函数
Student(char *name, int age) {
// this->name = name;
// 堆区 malloc代表在堆区开辟空间
this->name = (char *) (malloc(sizeof(char *) * 10));
// 字符串拷贝api
strcpy(this->name, name);
this->age = age;
cout << "两个参数的构造函数" << endl;
}
~Student() {
cout << "析构函数" << endl;
// 必须释放 堆区开辟的成员
if (this->name) {
free(this->name);
this->name = NULL; // 执行NULL的地址,避免出现悬空指针
}
}
四、拷贝构造函数
在了解什么是拷贝构造函数之前我们先来看一段代码
int main() {
Student student ("刘德华",50);
Student student1 = student;
cout << "student1的name = " <<student1->getName() <<" age = "<< student1->getAge() << endl;
return 0;
}
猜一猜这样打印student1的name和age是否有值.
image.png
打印结果是有值的,但是为什么呢. 这个就是C++的拷贝构造函数,在我们看不到地方C++已经帮我们作了一些工作,它会先去找student对应属性的值,在把这些值赋值给student1对应的属性.
拷贝构造函数是隐式的,C++已经默认帮我实现了, 但是我也可以覆盖拷贝函数,实现自己想要的拷贝函数,怎么做呢.
// 覆盖拷贝构造函数
Student(const Student & student) { // 常量引用:只读的,不让你修改
cout << "拷贝构造函数" << endl;
// 我们自己赋值
// 为什么要自己赋值,自己来控制,就可以 例如:-10
this->name = student.name;
this->age = student.age - 10;
cout << "自定义拷贝构造函数 内存地址 " << &student << endl;
}
覆盖拷贝构造函数后 我们重新来执行下刚才代码 看看结果
image.png
可以看到age的值为40,而且拷贝构造函数也执行了
我接下来看另外一个段代码 大家猜下age会不会减10;
Student *student1 = new Student("刘德华", 50);
Student *student2 = student1;
cout << "student1的name = " <<student1->getName() <<" age = "<< student1->getAge() <<
image.png
结果没有减10 而且拷贝构造函数也没有执行这是为什么呢?
大家看下这段断代码 Student *student2 = student1; 有没有发现段代码其实是定义了一个student2 指针指向student1指针所对应的地址,而不是拷贝函数,就明白为什么没有减10 拷贝函数也为什么没有执行.
五、常量指针、指针常量、常量指针常量
1.常量指针
image.png
常量指针不允许去修改【常量指针】存放地址所对应的值, 允许重新指向【常量指针】存放的地址
按我的理解就是常量对应指针的地址是不能变的 所以叫常量指针(常量的对应的地址或者指针不能变)
2.指针常量
image.png
指针常量 允许去修改【指针常量】存放地址所对应的值 ,不允许重新指向【指针常量】存放的地址
按我的理解就是指针的地址不能变,指针是个常量.
3.常量指针常量
image.png
常量指针常量 不允许去修改【常量指针常量】存放地址所对应的值也不允许重新指向【常量指针常量】存放的地址 就是指针是常量 指针对应的值也是常量 两个定死了 都不能修改.