天天看点

C++基本概念(1)

1.  内联函数:

     定义函数时,编译器通常在内存中生成函数代码的单一拷贝。程序调用函数时,程序控制传入这个函数,执行函数代码。函数结束后,执行返回调用程序后面一行。代码不是在需要函数时每次重复相同代码。而是一次编写,在调用时调用。如何函数调用十次,则程序每次访问相同指令集,只要一次拷贝而不是十个拷贝。

    这个做法节省内存空间,但是调用函数会给计算机资源带来一定的开销。

   C++用关键字inline避免函数调用。如果用关键字inline声明函数,则编译器之间将内联函数的语句复制到程序中调用函数点的程序中。

总结:像宏一样替换;

2.重载函数

在C中每个函数都都有自己 唯一的名字,虽然程序比较大那么函数名很多。C++可以有多个同名函数,但参数有区别,这就称为重载函数。

void  sum(int  a, int  b);

void sum(float a ,  float b);

这两个函数当作不同的函数处理,这就是重载;

3  对象与类

  C++中的类可以看作c中结构体struct 的升级版。C中 结构体是一种构造数据类型,可以包含若干个成员,每个成员类型不一样。可以通过结构体来定义结构体变量,每个变量有相同的性质。

#include <stdio.h>

int main(){
    // 定义结构体 Student
    struct Student{
        // 结构体包含的变量
        char *name;
        int age;
        float score;
    };

    // 通过结构体来定义变量
    struct Student stu1;
    // 操作结构体的成员
    stu1.name = "小明";
    stu1.age = 15;
    stu1.score = 92.5;
 
    printf("%s的年龄是 %d,成绩是 %f\n", stu1.name, stu1.age, stu1.score);

    return 0;
}      

C++类是一种构造数据类型,但是进行了一些扩展,类的成员不但可以是变量,还可以是函数; 通过类定义出来的变量也有特定的称呼,叫做“对象”。

#include <stdio.h>

int main(){
    //通过class关键字类定义类
    class Student{
    public:  //类包含的变量
        char *name;
        int age;
        float score;
   
    public:  //类包含的函数
        void say(){
            printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
        }
    };

    //通过类来定义变量,即创建对象
    class Student stu1;  //也可以省略关键字class
    //操作类的成员
    stu1.name = "小明";
    stu1.age = 15;
    stu1.score = 92.5f;
    stu1.say();

    return 0;
}      

构造函数: 生成与初始化对象;

析构函数:析构对象,去配自由存储空间;

复制构造函数:生成对象的准确拷贝;

重载赋值运算符: 将一个对象赋予另一个对象;

4.  构造函数

构造函数:程序中要编写显示函数调用语句,初始化类的数据成员最好能在生成对象时让对象自动化初始化,而不必另外进行函数调用。这种自动初始化是由所谓构造函数的特殊成员函数完成的。构造函数是个成员函数,在生成对象时自动执行。

5. 析构函数

  析构函数在对象要删除时自动调用,主要用于释放构造函数分配的内存资源。析构函数与类名相同,但是在前面加上~,如~Point()。

析构函数不能被重载

6.  复制构造函数

在一般的数据类型中, 我们经常会用一个变量来初始化另一个变量, 例如:

int a = 10;
        int b = a;      

    使用a变量来初始化b变量, 同样, 对于类创建的对象也可以用这种方式使用一个对象去初始化另一个对象。例如还在上篇中介绍的 Point 类中, 使用一个对象去初始化另一个对象:

#include "Point.h"
 2 
 3     int main()
 4     {
 5         Point M(10, 20);
 6         Point N = M;        //使用对象 M 初始化对象 N
 7         N.printPoint();
 8 
 9         return 0;
10     }      

    编译运行的结果:

xPos = 10
        yPOs = 20

        Process returned 0 (0x0)   execution time : 0.462 s
        Press any key to continue.      

7.this指针

this是c++中的一个关键字,也是一个常量指针,指向当前对象。通过this,可以访问当前对象的成员变量和成员函数。

Student stu;  //通过Student类来创建对象
Student *pStu = &stu;      
class Student{
private:
    char *name;
    int age;
    float score;

public:
    void setname(char *);
    void setage(int);
    void setscore(float);
};

void Student::setname(char *name){
    this->name = name;
}
void Student::setage(int age){
    this->age = age;
}
void Student::setscore(float score){
    this->score = score;
}      

本例中,函数参数和成员变量重名是没有问题的,因为通过 this 访问的是成员变量,而没有 this 的变量是函数内部的局部变量。例如对于this->name = name;语句,赋值号左边是类的成员变量,右边是 setname 函数的局部变量,也就是参数。

8.静态数据成员

静态数据成员实际上是类域中的全局变量。所以,静态数据成员的定义(初始化)不应该被放在头文件中。

9.静态成员函数

静态成员函数只能访问所在类的静态成员(数据和函数)。

静态成员函数可以直接访问该类的静态数据和函数成员,而访问非静态数据成员必须通过参数传递的方式得到一个对象名,然后通过对象名来访问。

10.常量成员函数

    常量成员函数的含义(成员函数 + const)

       在类的 非 常量成员函数中,this指针类型是 X *const(指    针常量),而在 常量成员函数中,this指针类型是const X *const     不能被修改,这就是常量成员函数和非常量成员函数的区别.

11.  友元函数

在实现类之间数据共享时,减少系统开销,提高效率。如果类A中的函数要访问类B中的成员(例如:智能指针类的实现),那么类A中该函数要是类B的友元函数。

具体来说:

为了使其他类的成员函数直接访问该类的私有变量。即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。

实际上具体大概有下面两种情况需要使用友元函数:

(1)运算符重载的某些场合需要使用友元。

(2)两个类要共享数据的时候。

12. 重载运算符

所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能。

实际上,我们已经在不知不觉中使用了运算符重载。例如,"+"号可以对不同类型(int、float 等)的数据进行加法操作;"<<"既是位移运算符,又可以配合 cout 向控制台输出数据。C++已经对这些运算符进行了重载。

C++ 也允许程序员自己重载运算符,这给我们带来了很大的便利。

13. 重载输入、输出运算符

C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型。您可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。

在这里,有一点很重要,我们需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。

14.   继承

在C++中,所谓“继承”就是在一个已存在的类的基础上建立一个新的类。已存在的类称为“基类(base class)”或“父类(father class)”,新建的类称为“派生类(derived class)”或“子类(son class )”。

前面介绍了类,一个类中包含了若干数据成员和成员函数。在不同的类中,数据成员和成员函数是不相同的。但有时两个类的内容基本相同或有一部分相同,例如巳声明了学生基本数据的类Student:

class Student
{
public:
   void display( )    //对成员函数display的定义
   {
      cout<<"num: " <<num<<endl;
      cout<<"name: "<< name <<endl;
      cout <<"sex: "<<sex<<endl;
   }
private:
   int num;
   string name;
   char sex;
};      

如果学校的某一部门除了需要用到学号、姓名、性别以外,还需要用到年龄、地址等信息。当然可以重新声明另一个类class Student1:

class Student1
{
public:
   void display( )  //此行原来已有
   {
      cout<<"num: " <<num<<endl;  //此行原来已有
      cout<<"name: "<< name <<endl;  //此行原来已有
      cout <<"sex: "<<sex<<endl;  //此行原来已有
      cout <<"age: "<<age<<endl;
      cout <<"address: "<<addr<<endl;
   }
private:
   int num;  //此行原来已有
   string name;  //此行原来已有
   char sex;  //此行原来已有
   int age;
   char addr[20];
};      

可以看到有相当一部分是原来已经有的,可以利用原来声明的类Student作为基础,再加上新的内容即可,以减少重复的工作量。C++提供的继承机制就是为了解决这个问题。

15.  继承与虚函数

虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段。而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个间接的指针去存取基类对象中的字段。

当编译器对程序进行编译碰到虚函数时,将不会赋予一个地址,而是插入一段汇编代码。每个包含虚函数的类都会由编译器产生一个虚函数表和一个虚函数表指针,其中虚函数表指针放在每个类的首地址处。当程序执行时,碰到对虚函数的调用,则通过插入的汇编代码到当前类的地址中找到虚函数表指针,通过虚函数的序号找到需要调用的虚函数。注意,一个系列的类的虚函数表中某一个函数的序号是一样的。而且,编译器会保证在使用父类指针操作子类对象时只能在父类已有的虚函数上实现虚函数的机制。

16.模版函数

我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。

//函数1.

int max(int x,int y);

{return(x>y)?x:y ;}

//函数2.

float max( float x,float y){

return (x>y)? x:y ;}

//函数3.

double max(double x,double y)

{return (c>y)? x:y ;}

但如果在主函数中,我们分别定义了 char a,b; 那么在执行max(a,b);时 程序就会出错,因为我们没有定义char类型的重载版本。

现在,我们再重新审视上述的max()函数,它们都具有同样的功能,即求两个数的最大值,能否只写一套代码解决这个问题呢?这样就会避免因重载函数定义不 全面而带来的调用错误。为解决上述问题C++引入模板机制,模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。

继续阅读