美文网首页
c++语法1

c++语法1

作者: yangzai | 来源:发表于2018-02-03 14:00 被阅读7次

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

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

相关文章

  • C++萌新到大牛,要看哪些书?

    初级阶段: 1. C++基础语法:《C++ Primer 第五版》 C++语法太过繁杂,很多语法特性一辈子也用不上...

  • C++循环与决策

    Tags:C++,《C++ Primer Plus》笔记 一、循环## 语法### C++中有三种循环语句,语法与...

  • c++语法1

    android底层是用c++实现的,java实现的是应用程层,为了后续学习android系统底层的源码,首先要了解...

  • C++语法系列之1

    今天开始C++语法系列第一篇,讲解C++基础语法。 1 使用预处理器指令避免重复包含头文件 方法1: 方法2: 2...

  • 论golang是世界上最好的语言

    概述 golang is a better C and a simple C++ golang主要特性 1、语法简...

  • 在Objective-C代码里面使用C++代码

    OC文件,不认识C++语法,只认识OC语法和C语法,使用了C++语法编译会报错 解决:后缀名改为.mm,例如把ma...

  • C到C++的升级

    1、C++ C++继承了所有的C特性 C++在C的基础上提供了更多的语法和特性 C++的设计目标是运行效率与开发效...

  • cpp1 封装和this指针

    Cpp1 封装和this指针 封装 C语言和C++语言的区别 C++是对C的补充扩展,C原有的语法C++都支持,并...

  • Java语言的特点

    1.简单性 java语法是c++语法的一个“纯净”版本,没有头文件、指针运算(甚至是指针语法)、结构、联合、操作符...

  • Java学习笔记1(基本程序设计结构)

    1、Java语言特性 1.简单性 Java算是C++语法的一个“纯净”版本了,里没有头文件、指针运算(甚至指针语法...

网友评论

      本文标题:c++语法1

      本文链接:https://www.haomeiwen.com/subject/jtzyzxtx.html