zoukankan      html  css  js  c++  java
  • C++之基础知识20170830

    /******************************************************************************************************************/

    一、C++类的引入

    与C相比,

    1.编译使用g++代替 gcc,执行在linux中还是一样的

    2.c++里面也有struct并对其进行了扩展,struct中的函数可以直接使用其成员,并可在struct中直接实现,

    例:

    struct person {

             char *name;

             int age;

             char *work;

             void printInfo(void)

             {

                       printf("name = %s, age = %d, work = %s ", name, age, work);

             }

    };

    int main(int argc, char **argv)

    {

             struct person persons[] = {

                       {"zhangsan", 10, "teacher"},

                       {"lisi", 16, "doctor"},

             };

             persons[0].printInfo();

             persons[1].printInfo();

             return 0;

    }

    3.这样的struct可以使用class代替,注意权限问题,class还有新的特性,

    例:

    class person {

    public:

    //注意权限问题

             char *name;

             int age;

             char *work;

             void printInfo(void)

             {

                       printf("name = %s, age = %d, work = %s ", name, age, work);

             }

    };

    /******************************************************************************************************************/

    二、C++基础知识_访问控制

    C++有三种访问权限private,protected,public

    1.默认权限是private

    2.对象的创建

    类 对象

    例:Person mPer;

    3.访问本类中的属性,使用this->

    由于c和c++里面有就近原则,所以这样就可以使用同名

    访问父类的属性,直接使用该属性名即可(或者使用 父类名::属性名 的方式来访问)

    /******************************************************************************************************************/

    三、C++基础知识_程序结构

    1.类中的成员函数在类中可不定义只是声明,实现放在类外面(类名 :: 函数名)

    例:

    class Person {

    private:

             char *name;

             int age;

             char *work;

    public:

             void setName(char *name);

             int setAge(int age);

             void printInfo(void);

    };

    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 ", name, age, work);

    }

    int main(int argc, char **argv)

    {

             Person per;

             //per.name = "zhangsan";

             per.setName("zhangsan");

             per.setAge(200);

             per.printInfo();

             return 0;

    }

    2. 实现放在类外面,这样就可以优化整个程序结构,

    把类的定义(函数成员只是声明)放在头文件中

    使用者包含这个头文件,就知道怎么用了(不去关心具体实现)

    具体实现放在具体实现文件里,包含对应头文件。

    Linux下的编译方式

    person: main.o person.o

             g++ -o $@ $^     //$@表示person(目标),$^表示所有的依赖

    %.o : %.cpp

             g++ -c -o $@ $<    //$<表示第一个依赖

    3.为防止出现函数名一致(函数名重复),导致不知道具体使用哪一个函数,引入命名空间namespace,

    1).用法:

    在定义和声明的地方,用namespace包含起来,例

    namespace A

    {

    }

    2).调用的时候使用A::函数名

    注意使用A中的类创建对象也要加上A,即A::Person per;

    per.setName();

    3).如果命名空间之间的类名没有冲突,

    类创建对象是可以不使用A::Person per;

    使用using声明,即using A::Person;(一般放在文件前面)

    文件前面代表的是全局命名空间,所以

    /* 把A::Person放入global namespace, 以后可以使用Person来表示A::Person */

    4).如果命名空间之间的类名没有冲突,

    也可以使用using编译,即using namespace A;(一般放在文件前面)

    把A空间里面所有的内容都导进来

    出现冲突的地方必须指明函数所在的命名空间(A::函数名)

    4.C++打印使用cout<<endl; 代替printf,

    例:

    std::cout<<"name = "<<name<<" age = "<<age<<" work = "<<work<<std::endl;

    注意

    1).需包含头文件

    #include <iostream>

    2).由于使用了命名空间,要指明cout的命名空间为标准命名空间里的

    std::cout<<"name = "<<name<<" age = "<<age<<" work = "<<work<<std::endl;

    或在文件前面指明using namespace std;

    /******************************************************************************************************************/

    四、C++基础知识_重载_指针_引用

    1.重载

    函数名一样,参数不一样

    2.指针

    与C语言一样

    3.引用(类似内存地址,使用和变量名一样)

    C++引入引用就是为了避免使用指针(防止指针使用不恰当),其实就是操作同一块内存

    例:

    int a=100;

    int &b=a;//b就是a的引用

    b是a的别名,操作b也就是操作a

    定义:

    int add_one_ref(int &b)

    {

    b = b+1;

    return b;

    }

    使用:cout<<add_one_ref(a)<<endl;

    注意,引用定义的时候就必须初始化,即赋值,同时引用是变量的别名,所以只能使用变量来赋值

    /******************************************************************************************************************/

    五、C++基础知识_构造函数

    1.构造函数:在类中,和类名名字相同的函数,可以带参数也可不带

    1).带参数的,创建对象时可传入,

    Person per("zhangsan", 16);

    2).无传入参数的,

    Person per2;

    //Person per2();//不使用这个,这个表示的是函数名为per2的函数的声明

    2.不想写很多的重载的构造函数的情况

    Person(char *name, int age, char *work = "none")

    {//表示如果没有传入第三个参数,则第三个参数为"none"

    cout <<"Pserson(char*, int)"<<endl;

    this->name = name;

    this->age = age;

    this->work = work;

    }

    Person per("zhangsan", 16);//导致this->work ="none"

    3.除Person per("zhangsan", 16);方式外,创建对象的方式

    使用指针的方式:

    Person *per4 = new Person;

    //这两种创建对象的方式一样,都是调用无参构造函数

    Person *per5 = new Person();

    //这两种创建对象的方式一样,都是调用无参构造函数

    Person *per6 = new Person[2];

    //per6是个对象数组,数组成员是对象     

    Person *per7 = new Person("lisi", 18, "student");

    Person *per8 = new Person("wangwu", 18);

    per.printInfo();

    per7->printInfo();

    per8->printInfo();

    //动态创建的对象(new出来的对象),整个程序(进程)执行完后会自动释放所有对象占用的内存空间(包括子函数中创建的对象),如果要手动释放,可以用delete命令:  

    delete per4;

    delete per5;

    delete []per6;

    //对象数组的删除

    delete per7;

    delete per8;

    //不是new的对象,局部变量:函数执行完会自动销毁,全局变量:一直存在

    4.分配堆空间,使用new代替malloc

    分配:

    this->name = new char[strlen(name) + 1];

    strcpy(this->name, name);

    释放:

    delete this->name;

    linux中,free命令查看空闲内存

    5.析构函数(只能有一个)

    释放对象时,对象所占据的内存释放了,如果对象的函数成员中又new了对象,同时又没有对函数成员中new的对象delete,那么就会造成内存泄漏。

    比较方便解决这个问题,就是引入析构函数,析构函数中加入对函数成员中new的对象的delete操作,这样delete对象时就释放了函数成员中new的对象(delete销毁对象的同时就会调用析构函数)

    例:

    ~Person()

    {

    if (this->name)

    delete this->name;

    if (this->work)

    delete this->work;

    }

    注意使用new创建的对象,用完要用delete释放

    6.默认拷贝构造函数

    拷贝构造函数和赋值函数的区别:拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。

    String a("A");

    String b("B");

    String c = a; // 调用了拷贝构造函数,最好写成c(a);

    c = b; // 调用了赋值函数

     

    拷贝构造函数分为深拷贝和浅拷贝,深浅拷贝:https://blog.csdn.net/libin66/article/details/53140284

    无参数时,默认构造函数

    有对象作为参数时,默认拷贝构造函数(进行值的拷贝,即浅拷贝) //C++类有四个默认函数---构造函数、拷贝构造函数(浅拷贝)、析构函数、赋值函数(浅拷贝):https://blog.csdn.net/anye3000/article/details/6570445)

    Person per("zhangsan", 18);

    Person per2(per);// 有对象参数, per是对象

    默认拷贝构造函数,会改变其中的值,则在释放的时候,同一个值对应的堆空间会被释放两次,所以要定义自己的拷贝构造函数

    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);

    }

    7.对象的构造顺序

    1).创建对象首先是全局变量(在main函数执行之前),然后碰到哪个就创建哪个(不管是否静态),即按运行中定义对象的顺序调用构造函数。

    由于静态对象的生存周期,退出函数不会被销毁,同时再次进入函数由于已经创建了也不会再次创建,即只定义一次

    2).类中的数据成员是对象的情况,

    I、先调用成员对象的构造函数,再调用自己的构造函数。

    调用成员对象的无参构造函数,无需添加什么会自动调用

    II、调用成员对象(数据成员是对象)的有参构造函数的方法

    在自己类的构造函数后面加上:号,例:

    class Student {

    private:

    Person father;//定义顺序

    Person mother;

    int student_id;

    public:

    Student()

    {

    cout<<"Student()"<<endl;

    }

    Student(int id, char *father, char *mother, int father_age = 40, int mother_age = 39) : mother(mother, mother_age), father(father, father_age)

    {//加上:号就会去调用father对象的有参构造函数,修改:号后面的对象顺序不影响构造顺序,构造顺序按照定义的顺序,即先father,再mother,最后自己的         

            

    cout<<"Student(int id, char *father, char *mother, int father_age = 40, int mother_age = 39)"<<endl;

    }

    ~Student()

    {

    cout<<"~Student()"<<endl;

    }

    };

    III、析构函数的调用顺序

    和构造函数的调用顺序相反

    /******************************************************************************************************************/

    六、C++基础知识_静态成员_友员

    静态成员是属于类的(放在类的类名空间中),只占一份空间,不属于对象

    1.类中定义

    private:

    static int cnt; //静态成员

    public:

    static int getCount(void)

    { //静态成员用于访问静态成员变量

    //静态成员函数不能访问非静态成员变量(还没定义分配)    

                      return cnt;

    }

    2.使用

    一般使用成员函数访问:Person::getCount()

    如果是公有属性,也可以Person::cnt;

    3.class是struct的进化,所以还需要分配空间

    int Person::cnt = 0; /* 定义和初始化 不再需要加static*/

    //同时要放在main之前(放在main内会编译出错),因为要在创建对象之前就定义和初始化

    4.也可以在类外定义静态成员函数

    public:

    static int getCount(void);//类内部声明

    int Person::getCount(void)

    { //类外部定义,不再需要加static     

    return cnt;

    }

    5.也可以使用类的实例化对象调用静态成员函数

    cout << "person number = "<<Person::getCount()<<endl;

    cout << "person number = "<<p[0].getCount()<<endl;//类的实例化对象调用静态成员函数

    cout << "person number = "<<p[1].getCount()<<endl;

    6.友元

    1.Point(int x, int y) : x(x), y(y) {}

    等价于

    Point(int x, int y){this->x=x,this->y=y}

    2.类的友元函数(该函数一般会操作该类)可以直接访问该类的私有成员

    1)首先在类中声明是友元函数

    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);

    };

    2)友元函数就可以直接使用类的私有成员了

    Point add(Point &p1, Point &p2)

    {

    Point n;

    n.x = p1.x+p2.x;

             n.y = p1.y+p2.y;

    return n;

    }

    /******************************************************************************************************************/

    七、C++基础知识_运算符重载_类外函数

    1.加减乘除运算的重载

    例如+号的重载

    1).定义:

    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;

    }

    2).使用

    Point p1(1, 2);

    Point p2(2, 3);

    Point sum = p1+p2;

    2.++p,p++的重载

    1).定义:

    /* Point p(1,2); ++p; */

    Point operator++(Point &p)

    {

    cout<<"++p"<<endl;

    p.x += 1;

    p.y += 1;

    return p;

    }

    /* Point p(1,2); p++; */

    Point operator++(Point &p, int a)

    {//参数不同实现重载

    cout<<"p++"<<endl;

    Point n;

    n = p;

    p.x += 1;

    p.y += 1;

    return n; 

    }

    2).使用

    Point p1(1, 2);

    Point p2(2, 3);

    Point n = ++p1;

    n.printInfo();

    p1.printInfo();

    cout << "******************"<<endl;

    Point m = p2++;

    m.printInfo();

    p2.printInfo();

    4.返回值是对象时,返回时会创建临时对象的问题(临时对象是局部变量用完又会被销毁)

    /* Point p(1,2); ++p; */

    Point operator++(Point &p)

    {

    cout<<"++p"<<endl;

    p.x += 1;

    p.y += 1;

    return p;//返回引用,返回类型又是对象,系统就会自动创建临时对象,用完销毁(整个过程会调用构造函数与析构函数,浪费时间)

    }

    改进:

    /* Point p(1,2); ++p; */

    Point& operator++(Point &p)

    {//使用引用则不会再去创建

    cout<<"++p"<<endl;

    p.x += 1;

    p.y += 1;

    return p;//使用引用则不会再去创建

    }

    Point operator++(Point &p, int a)

    {//参数不同实现重载

    cout<<"p++"<<endl; //endl表示换行

    Point n;

    n = p;

    p.x += 1;

    p.y += 1;

    return n;//已经创建了n(已经有临时变量了)则不会再去创建临时对象 

    }

    5.使用返回对象还是引用,取决于是否影响程序执行结果

    引用返回效率高,在不影响运算结果则效率优先,即使用引用

    6.<<的重载(<<也是运算符)

    1)定义

    ostream& operator<<(ostream &o, Point p)

    {

    cout<<"("<<p.x<<", "<<p.y<<")";//不需要换行

    return o;

    }

    2)使用

    cout<<m<<" "<<n<<endl;

    //<<m表示执行<<m,并将结果输出到ostream

    //<<m<<n表示把先执行<<m得到的ostream与n一起作为<<的参数,从而得到新的ostream引用,即<<n执行的结果追加到之前执行的<<m的结果后面

    //cout<<m<<" "<<n<<endl,表示cout使用 执行<<m<<" "<<n<<endl的返回值,即<<m<<" "<<n<<endl的返回值输出到cout

    7.运算符重载函数可直接调用

    operator++(p1,0);//后一个参数随便传入值,只是为了分辨

    operator<<(cout,p1);

    /******************************************************************************************************************/

    八、C++基础知识_运算符重载_成员函数

    1.类中定义重载函数

    Point operator+(Point &p)

    {

    cout<<"operator+"<<endl;

    Point n;

    n.x = this->x + p.x; //类中所以可以这么用 p.x

    n.y = this->y + p.y;

    return n;

    }

    /* Point p(1,2); ++p; */

    Point& operator++(void)

    {

    cout<<"operator++(void)"<<endl;

    this->x += 1;

    this->y += 1;

    return *this; //返回别名,变量名

    }

    /* Point p(1,2); p++; */

    Point operator++(int a)

    {

    cout<<"operator++(int a)"<<endl;

    Point n;

    n = *this;

    this->x += 1;

    this->y += 1;

    return n; 

    }

    2.使用

    Point p1(1, 2);

    Point p2(2, 3);

    Point m, n;

    m = p1 + p2; /* m = p1.operator+(p2); */

    cout<<"add p1,p2 = "<<m<<endl;

    cout<<"begin"<<endl;

    m = ++p1;    /* m = p1.operator++(); */

    cout<<"m = "<<m<<" "<<"p1 = "<<p1<<endl;

    cout << "******************"<<endl;

    n = p1++;    /* m = p1.operator++(0); */

    cout<<"n = "<<n<<" "<<"p1 = "<<p1<<endl;

    cout<<"end"<<endl;

    3.重载等号

    对象拷贝时,可能会导致两个对象中的指针指向同一块内存,就有可能导致同一块内存被释放两次(或者覆盖导致,导致原有的内存泄漏)

    重载等号可解决这个问题

    1.类中定义

    Person& operator=(const Person& p)

    {

    cout << "operator=(const Person& p)"<<endl;

    if (this == &p)

    return *this;

    this->age = p.age;

    if (this->name)

    {

    delete this->name;

    }

    if (this->work)

    {

    delete this->work;

    }

    this->name = new char[strlen(p.name) + 1];

    strcpy(this->name, p.name);

    this->work = new char[strlen(p.work) + 1];

    strcpy(this->work, p.work);

    return *this;

    }

    2.使用

    const Person p1("zhangsan", 10);

    cout<<"Person p2 = p1" <<endl;

    Person p2 = p1;

    //调用拷贝构造函数,没有调用重载等号函数

    //注意拷贝构造函数的参数要加上const,来兼容只读的const参数(const对象作为参数)const对象只能调用const函数(函数声明后加上const,表示这个函数不会有修改的操作,不会修改类中的变量)     

    Person p3;

    cout<<"p3=p1"<<endl;

    p3 = p1;//调用重载等号函数

    cout<<"end"<<endl;

    //p3=(p2=p) 再次赋值的情况,所以重载函数需要返回值

  • 相关阅读:
    C#对象初始化器
    C#构造方法
    C#方法重载
    C#方法
    C#类 对象 字段和属性
    C#数组
    C#字符串
    C#原码反码补码
    字段、方法、属性
    单例模式、异常
  • 原文地址:https://www.cnblogs.com/yuweifeng/p/7455036.html
Copyright © 2011-2022 走看看