android底层是用c++实现的,java实现的是应用程层,为了后续学习android系统底层的源码,首先要了解c++的语法。
这里只是简单的快速了解C++,作为学习笔记
- 访问控制:
C++中是通过关键字: public 、private、 protected ,以及命名空间实现。java中也是通过这三个关键字加package实现。我们封装Person信息:
person.cpp:
#include <stdio.h>
// my first program in C++
class Person {
private:
char *name;
int age;
char *work;
public:
void setName(char *name){
this->name = name;
}
int setAge(int age){
if (age < 0 || age > 150){
this->age = 0;
return -1;
}
this->age = age;
return 0;
}
void printInfo(void){
printf("name = %s, age = %d, work = %s\n", name, age, work);
}
};
int main(int argc, char **argv){
Person per;
per.setName("zhangsan");
per.setAge(200);
per.printInfo();
return 0;
}
我们将成员使用private隐藏起来,只提供公用方法。
- 程序结构:
在person.cpp中我们可以将定义与实现分离出来,这样让使用Person类的人不用关心具体是怎么实现的。定义我们使用.h文件。
person.h
#include <stdio.h>
class Person {
private:
char *name;
int age;
char *work;
public:
void setName(char *name);
int setAge(int age);
void printInfo(void);
};
person.cpp实现:
#include <stdio.h>
#include "person.h"
void Person::setName(char *name){
this->name = name;
}
int Person::setAge(int age){
if (age < 0 || age > 150)
{
this->age = 0;
return -1;
}
this->age = age;
return 0;
}
void Person::printInfo(void){
printf("name = %s, age = %d, work = %s\n", name, age, work);
}
对于使用者,只需要引入person.h
#include <stdio.h>
#include "person.h"
int main(int argc, char **argv){
Person per;
per.setName("zhangsan");
per.setAge(200);
per.printInfo();
return 0;
}
除此之外,如果我们要提供两个同名Person类,一个是中国人,一个美国人。如果在java中我们可以通过引用不同包名解决类同名的问题,在c++中则使用命名空间来解决。实现Person时用
namespace C包裹,代表中国人:
namespace C {
Person ....
}
使用namespace U包裹,代表美国人:
namespace U {
Person ....
}
在需要时使用using C::Pserson或using U::Pserson引入。引入位置可以是全局的,也可以是局部的。引入可以是类或者类中的方法。
- 重载、指针、引用:
- 重载:函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。参数列表不同包括参数的个数不同、类型不同或顺序不同,仅仅参数名称不同是不可以的。函数返回值也不能作为重载的依据
#include <iostream>
using namespace std;
int add(int a, int b){
cout<<"add int+int"<<endl;
return a+b;
}
int add(int a, int b, int c){
cout<<"add int+int+int"<<endl;
return a+b+c;
}
double add(double a, double b){
cout<<"add double+double"<<endl;
return a+b;
}
double add(int a, double b){
cout<<"add int+double"<<endl;
return (double)a+b;
}
double add(double b, int a){
cout<<"add double+int"<<endl;
return (double)a+b;
}
int main(int argc, char **argv){
add(1, 2);
add(1, 2, 3);
add(1.0, 2.0);
add(1, 2.0);
add(1.0, 2);
return 0;
}
- 操作符重载:
例如+操作符不支持Point类,我们可以重载+操作符实现Point相+
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point() {}
Point(int x, int y) : x(x), y(y) {}
int getX(){ return x; }
int getY(){ return y; }
void setX(int x){ this->x = x; }
void setY(int y){ this->y = y; }
void printInfo(){
cout<<"("<<x<<", "<<y<<")"<<endl;
}
//这里使用友元函数,请看下一篇
friend Point add(Point &p1, Point &p2);
friend Point operator+(Point &p1, Point &p2);
};
Point add(Point &p1, Point &p2){
Point n;
n.x = p1.x+p2.x;
n.y = p1.y+p2.y;
return n;
}
//重载操作符+
Point operator+(Point &p1, Point &p2){
cout<<"Point operator+(Point &p1, Point &p2)"<<endl;
Point n;
n.x = p1.x+p2.x;
n.y = p1.y+p2.y;
return n;
}
int main(int argc, char **argv){
Point p1(1, 2);
Point p2(2, 3);
Point sum = p1+p2;
sum.printInfo();
return 0;
}
输出:
Point operator+(Point &p1, Point &p2)
(3, 5)
对于+-*/都使用类似方式实现重载。
- 指针、引用:
#include <iostream>
using namespace std;
int add_one(int a){
a = a+1;
return a;
}
int main(int argc, char **argv){
int a = 99;
cout<<add_one(a)<<endl;
cout<<"a = "<<a<<endl;
return 0;
}
我们调用main时,输出 100 a = 99;之所以a的值没有改变是因为我们传递的a与add_one方法中的a不是一个a,我们传递的是实参,方法中的是形参,实参传递的是值,形参的改变是不会影响实参的。
如果我们以指针的方式传递会怎样呢?
#include <iostream>
using namespace std;
int add_one(int *a){
*a = *a + 1;
return *a;
}
int main(int argc, char **argv){
int a = 99;
cout<<add_one(&a)<<endl;
cout<<"a = "<<a<<endl;
return 0;
}
这里我们取址a,输出:100 a = 100;我们传递给方法是地址,方法中取出地址的值操作加1,其实操作是a本身,所有a的值也变化了。这就是指针操作。
我们知道c中的指针很强大,用不好破坏性很强,而c++中用引用解决这个问题:
int add_one_ref(int &b){
b = b+1;
return b;
}
int main(int argc, char **argv){
int a = 99;
cout<<add_one_ref(a)<<endl;
cout<<"a = "<<a<<endl;
return 0;
}
输出:100 a = 100; add_one_ref方法参数是&b,当a传入时,他会取址a,赋值给b,也就是b会指向a的地址,a和b共同指向一块内存地址。当b+1时也就是a+1,所有a的值被修改了。要注意的是:引用在定义时,不能赋值为具体数字或空,必须引用一个变量。int &c; 或int &c = 1;都是错误的。
- 构造函数与析构函数:
是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载
#include <iostream>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {cout <<"Pserson()"<<endl;}
Person(char *name) {
cout <<"Pserson(char *)"<<endl;
this->name = name;
}
Person(char *name, int age, char *work = "none") {
cout <<"Pserson(char*, int)"<<endl;
this->name = name;
this->age = age;
this->work = work;
}
void setName(char *n){
name = n;
}
int setAge(int a){
if (a < 0 || a > 150){
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void){
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv){
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
per.printInfo();
return 0;
}
输出:
Pserson(char*, int)
Pserson()
name = zhangsan, age = 16, work = none
这里我们在调用无参构造函数时不需要带() Person per2,如果是Person per2()则是函数的声明,类似int fun();,而不是调用构造方法了。
Person(char *name, int age, char *work = "none")这个构造函数定义了work的默认值。
除此之外我们还可以用指针来初始化对象,并且为成员分配空间。
#include <iostream>
#include <string.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {cout <<"Pserson()"<<endl;}
Person(char *name) {
cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
Person(char *name, int age, char *work = "none") {
cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
void setName(char *n){
name = n;
}
int setAge(int a){
if (a < 0 || a > 150){
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void){
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv){
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
Person *per4 = new Person;
Person *per5 = new Person();
Person *per6 = new Person[2];
Person *per7 = new Person("lisi", 18, "student");
Person *per8 = new Person("wangwu", 18);
per.printInfo();
per7->printInfo();
per8->printInfo();
delete per4;
delete per5;
delete []per6;
delete per7;
delete per8;
return 0;
}
输出:
Pserson(char*, int)
Pserson()
Pserson()
Pserson()
Pserson()
Pserson()
Pserson(char*, int)
Pserson(char*, int)
name = zhangsan, age = 16, work = none
name = lisi, age = 18, work = student
name = wangwu, age = 18, work = none
可以看到我们使用new 的方式创造的都会调用对应的构造函数,分配的堆内存可以手动释放。但是我们给成员分配的堆空间没有释放。我们可以使用析构函数释放堆空间
~Person(){
if (this->name)
delete this->name;
if (this->work)
delete this->work;
}
析构函数:
与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
拷贝构造函数:
又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。
构造函数与析构函数调用时机:这里我们将添加全局构造对象,main构造对象,局部构造对象,以及静态的。
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name) {
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none") {
cout <<"Pserson(char*, int), name = "<<name<<", age= "<<age<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
Person(Person &per) {
cout <<"Pserson(Person &)"<<endl;
this->age = per.age;
this->name = new char[strlen(per.name) + 1];
strcpy(this->name, per.name);
this->work = new char[strlen(per.work) + 1];
strcpy(this->work, per.work);
}
~Person(){
cout << "~Person()"<<endl;
if (this->name) {
cout << "name = "<<name<<endl;
delete this->name;
}
if (this->work) {
cout << "work = "<<work<<endl;
delete this->work;
}
}
void setName(char *n){
name = n;
}
int setAge(int a){
if (a < 0 || a > 150){
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void){
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
//全局
Person per_g("per_g", 10);
void func(){
//局部的
Person per_func("per_func", 11);
//局部静态
static Person per_func_s("per_func_s", 11);
}
int main(int argc, char **argv){
//main中
Person per_main("per_main", 11);
//main中静态
static Person per_main_s("per_main_s", 11);
for (int i = 0; i < 2; i++){
func();
Person per_for("per_for", i);
}
return 0;
}
输出:
//先调用全局构造
Pserson(char*, int), name = per_g, age= 10
//main构造
Pserson(char*, int), name = per_main, age= 11
//main静态
Pserson(char*, int), name = per_main_s, age= 11
//进入for循环,func函数中局部
Pserson(char*, int), name = per_func, age= 11
//局部静态
Pserson(char*, int), name = per_func_s, age= 11
//func函数结束后会调用func中per_func对象的析构函数,但是没有调用静态对象的析构函数!
~Person()
name = per_func
work = none
//for循环中创建对象
Pserson(char*, int), name = per_for, age= 0
//第一次for循环结束后执行for循环中per_for对象的析构函数
~Person()
name = per_for
work = none
//第二次for循环,进入func方法,但是没有创建func中静态对象
Pserson(char*, int), name = per_func, age= 11
//func函数结束后,执行析构
~Person()
name = per_func
work = none
//创建for循环中的对象
Pserson(char*, int), name = per_for, age= 1
//for循环结束后
~Person()
name = per_for
work = none
//main函数结束,但是没有执行main中的静态对象
~Person()
name = per_main
work = none
//调用func中的静态析构
~Person()
name = per_func_s
work = none
//调用main中的静态析构
~Person()
name = per_main_s
work = none
//调用全局的析构
~Person()
name = per_g
work = none
从上面的输出我们看到:
1 对于静态的实例化对象,如果已经创建过了,则不会再创建了。例如第二次for循环的时候就没有再次创建per_func_s静态对象。
2 静态对象只会在main结束前调用析构。
总结下构造顺序:按运行中定义对象的顺序调用构造函数,静态对象只调用一次构造函数,全局对象在main函数执行前被构造。
下一篇继续学习静态成员、友元函数、继承、多重继承,继承访问权限