c++ basic_1

编程与计算机科学


编程并不能跟计算机科学划等号,其仅仅是计算机科学领域最重要的工具和研究手段之一。

编程对于计算机科学的作用,就相当于望远镜对于天文学的作用。

计算机科学是知识,而编程是技能。学习技能的正确思路,应该是以动手练习为主,看书学习为辅

c++包含多个范式,例如面向对象编程,函数式编程,模板,以及继承自纯c语言的面向过程编程等。

“c++精通很难”这样的说法其实是一种误解,因为程序员不需要刻意去使用c++的各种特性,c++本身也并不强迫你必须掌握某些特性才能编写你的程序。
c++就像自助餐一样,各取所取即可,你不需要刻意地使用那些复杂的特性,只要能完成你的工作就行。

程序语言可以分为:

  • 机器语言
  • 汇编语言
  • 高级语言
    • 编译型语言
    • 解释型语言
解释器
编译器 + 连接器
高级语言
解释型语言
机器码或二进制代码
编译型语言

面向对象编程

面向对象编程是一种非常强大的,非常重要的编程范式,其可以极大地提高代码的重用性、灵活性和扩展性,是现代软件工程活动至关重要的基础之一。
现实中的一切都可以抽象成对象,无论简单和复杂。
面向对象编程有四个主要特点:抽象封装继承多态

抽象

面向对象中方法中的抽象,是指对一系列具体问题进行概括,抽出一类对象的公共性质并加以描述的过程。
对一个问题的抽象分两个方面:

  • 数据抽象
    • 描述某类对象的特性和状态
  • 行为抽象
    • 描述某类对象的功能或者行为特性

封装

封装就是将抽象得到的数据和行为(或者功能)相结合,形成一个有机的整体,也就是c++中的类(class),其中的数据和函数都是类的成员。

继承和多态

  • 继承的含义是,新建了一个继承了原有类的新类,具有原有类的所有特征的同时,又具有自身的新特性。
  • 多态则是面向对象程序设计,对于人类思维方式的一种直接模拟。从广义上说,多态性指的是同一段程序,可以直接处理多种类型对象的能力。

类和对象

  • 类是面向对象程序设计方法的核心——通过类,我们可以实现对于数据的封装和隐藏
  • 当我们定义了一个类之后,就可以定义这个类的变量,这个变量就是类的对象,或者叫做实例,而定义的过程,则成为实例化
  • 在类中定义的函数和数据,分别称为数据成员函数成员
class 类名称 {
public:
    外部成员
protected:
    保护型成员
private:
    私有成员
};

类和对象的使用

可在声明一个类Class ClName后,在其他地方通过ClName cname实例化类,获得类对象。

类的构造函数

构造函数是一种定义在类体中的特殊函数,它可以被用于对类进行初始化的算法。
在语法上,构造函数具有这样的性质:

  • 函数名与类名完全相同
  • 不能定义返回值类型,也不能有return语句;
  • 可以有形参,也可以没有形参,可以带有默认参数(称为默认构造函数
  • 可以重载

一般构造函数不需手动调用,在创建对象的时候,构造函数会被自动调用。

Clock MyClock(30, 10, 3);//自动调用类中的构造函数

默认构造函数

默认构造函数:参数为默认值,或无参数,例如:

Clock();
Clock(int newH = 0, int newM = 0, int newS = 0);

注意:同一个类中不能出现两个默认构造函数
如果我们在定义一个类的时候,不声明任何构造函数,那么编译器在编译的时候,就会为我们自动生成一个默认构造函数,该构造函数特点是“什么也不做”,例于Clock(){}

复制构造函数(又称拷贝构造函数)

复制构造函数是用一个已有的对象,来执行一个新对象的构造,其具有一般构造函数的所有特性——它的形参是本类的一个对象的引用,作用是用一个已经存在的对象来初始化一个新对象。
复制构造函数使用的是引用传递1

若我们未定义类的构造函数,则系统会在必要的时候,隐含生成默认复制构造函数,这个隐含的复制构造函数的作用是,把初始值对象的每一个数据成员的值都复制到新建的对象中,因此可以说是完成了对同类对象的复制

复制构造函数具体声明方式如下:

class 类名 {
public:
    类名(const 类名& 对象名) { 
        //实现
    }
};

具体代码示例:

class Point {
public:
    Point(const Point &p);
private:
    int x, y;
};

Point::Point(const Point &p) {
    x = p.x;
    y = p.y;
}

注意:以上代码中,复制构造函数通过一种看似“不合法”的方式,访问了Point类的实例对象p的两个私有成员变量。但private和public的区别是对类来说的,而不是对对象来说的。复制构造函数是Point类的成员函数,所以其能访问类的私有成员变量,这跟具体的对象无关。

普通构造函数(包括默认构造函数)是在对象创建的时候被调用,复制构造函数会在什么时候被调用?

  • 当用类的一个对象去初始化该类的另一个对象的时候:
    Point a(1, 2);
    Point b(a);//用对象a初始化b,复制构造函数被调用
    Point c = a;//用对象a给对象c赋值,复制构造函数被调用
    
  • 当函数的形参是类的对象,调用函数时进行形实结合的时候:
    void f(Point p) {
        //code here
    }
    int main() {
        Point a(1, 2);
        f(a);
        return 0;
    }
    
  • 当函数的返回值是类的对象,函数执行完成返回调用者的时候
    Point g() {
        Point a(1, 2);
        return a; //返回的并非a本身,而是在主调函数中用a重新构造的新对象
    }
    

复制构造函数所带来的

  • 浅复制——未能将对象的资源同时复制过来,即为浅复制,默认复制构造函数只能浅复制
  • 深复制——将对象的资源同时复制过来,即为深复制,需要自定义复制构造函数。

初始化列表和析构函数

初始化列表

初始化列表即为在类的构造函数的参数列表后面,写上要用哪个参数,来初始化哪个变量,写法如下:

构造函数(参数列表):成员变量(常量或者参数) {
    //构造函数的函数体
}

class foo
{
public:
    foo(string s, int i):name(s), id(i){  // 初始化列表
        //构造函数的函数体
    }
private:
    string name ;
    int id ;
};

初始化列表能够提高编码效率,能直接将要赋的值来初始化成员变量,若不用初始化列表,则要“先建立一个对象,初始化成员变量,然后再为成员变量一一赋值”,较为繁琐。
简言之,初始化列表在效率上相对于构造函数中的赋值,会存在一定优势,比如在调用开销的节约方面。

在实际的程序执行中,一个类被实例化成一个对象,整个过程实际上可分为两个阶段:

  1. 对象的构造和成员变量的初始化——初始化列表就是在此期间发挥作用的;
  2. 执行构造函数中的操作。

析构函数

想回收泄露的内存,只有两种办法:

  • 给内存重新通电(即重启机器),清除内存中的所有数据
  • 进程结束时,由操作系统负责回收它所使用的所有内存空间

析构函数的作用就是在对象结束的时候,自动调用该函数释放资源,避免出现内存泄漏
同样,如果没有自定义的析构函数,则编译器会自动生成一个默认的析构函数。
简言之,如果希望程序在对象被删除之前,自动地来完成某些事情,就可以把代码写到析构函数里,析构函数示例如下:

class Clock {
public:
    ~Clock() {
        //写入想在对象结束时自动执行的命令
    }
};

##命令空间
对于c++程序来说,标识符的作用域有:

  • 函数原型作用域

    c++中最小的作用域,在函数原型声明时形式参数的作用范围就是函数原型作用域

  • 局部作用域(块作用域)

    指的是同一函数体内,使用大括号分割的不同代码,较为常见的是选择结构和循环结构的局部作用域。

  • 类作用域

    类可以看作是一组有名字的成员的集合,具体说某一个类class成员member具有类作用域

  • 命名空间(namespace)

    一个命名空间自身即确定了一个作用域,凡是在该命名空间内声明的,不属于前面提到的各个作用域的标识符,都属于这个命名空间的作用域。

namespace的格式

namespace 命名空间名{
    命名空间内的各种声明(函数声明, 类声明等)
}

在命名空间内部可以直接引用当前命名空间的标识符,如果要引用其他命名空间的表示符,则用其他命名空间::标识符来引用。

namespace NS {
    class Class{
        //类成员
    };
}
NS::Class object;

使用c++中的using

using 命名空间::标识符名  //将其直接暴露在当前作用域,即可直接使用该标识符
using namespace 命名空间  //直接引用整个命名空间,即可直接使用空间内部的所有标识符

类似于函数,命名空间也是允许嵌套,比如:

namespace Outer {
    namespace Inner {
        class Class {
            //类成员
        };
    }
}

如果要引用内部的Class类,则这样应用:

using Outer::Inner::Class;

注:一般工程上不会直接使用using namespace std这样的写法,因为这种写法会造成命名空间污染,在实际开发中很容易导致不明确的行为(例如不可移植)。

类的组合

  • 类的组合描述的就是一个类内嵌其他对象作为成员的情况,他们之间的关系是一种包含和被包含的关系
  • 当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将会首先被自动创建。故创建对象时,既要对类中基本的数据类型进行初始化,也要对内嵌对象进行初始化。

组合类构造函数一般的定义形式如下:

类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表)...{
    //初始化函数体
}

当创建某类的对象时,该类调用构造函数的顺序为:

  1. 调用内嵌对象的构造函数,若有多个内嵌对象,则按照其在组合类中定义的顺序调用。(注意:各内嵌对象在参数列表中的顺序与实际的构造函数调用顺序无关)
  2. 本类的构造函数的函数体

对应的析构函数调用顺序与构造函数调用顺序是相反的。

前向引用声明和结构体

前向引用声明

类应该先声明后引用,但如果需要在某个类的声明之前引用该类,则应该进行前向引用声明前向引用声明只为程序引入一个标识符,但具体的声明在其他地方
示例如下:

class B;
class A {
public:
    void f(B b);
};
class B {
public:
    void g(A a);
};

注意:当使用前向声明时,只能使用被声明的符号,而不能涉及类的任何细节。例如:

class B;
class A {
public:
    B b; //错误,此时类B的定义尚不完善,但可以改为 B& b;
};
class B {
public:
    void g(A a);
};

结构体

  • 结构体是一种特殊形态的类,其和类的使用方法几乎一样。结构体内成员默认为公有成员。
  • 类和结构体并存,是由于历史原因造成的,但要是仅仅想把一系列不同类型的数据组合成一个整体,以便于保存,则可考虑用结构体,用类保存反而较为麻烦。

  1. 为什么这里要使用引用来传参,而不是采用值传递?值传递就是当函数调用时,给形参分配内存空间,然后用实参的值来初始化形参。如果参数是对象的话,那么对于值传递来说,“初始化形参”这个过程会造成额外的时间开销,浪费系统资源。而使用引用,则不会有这个问题。 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值