c++语法1

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函数执行前被构造。

下一篇继续学习静态成员、友元函数、继承、多重继承,继承访问权限

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • C++文件 例:从文件income. in中读入收入直到文件结束,并将收入和税金输出到文件tax. out。 检查...
    SeanC52111阅读 2,764评论 0 3
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,513评论 1 51
  • 1.面向对象的程序设计思想是什么? 答:把数据结构和对数据结构进行操作的方法封装形成一个个的对象。 2.什么是类?...
    少帅yangjie阅读 4,992评论 0 14
  • 2017.10.31 周二 晴 晚上乒乓球训练继续,可是因为儿子的语文预习笔记没有完成,就耽误了一些时间,等完成作...
    戴骁勇阅读 194评论 0 1
  • 我是喜欢花花草草的人,现在最大的愿望是开一家花店,门口弄的郁郁葱葱,忍不住停下脚步,有一天,我家门口多了一个东西,...
    勇敢面对007阅读 177评论 0 0