c++学习笔记之类和对象、构造析构拷贝构造函数、动态内存友元函数

2017-3-21
============
主题:
    1.类和对象
    2.封裝
    3.构造函数与析构函数
    4.标准库类型string
    5.static类成员
    6.动态内存分配
    7.拷贝构造函数
    8.const关键字
    9.友元函数与友元类
    10.使用valgrind检测内存泄漏

    [OOP(面向对象编程)的三大特点]
    [封装]
    [继承]
    [多态]

1.类(class)和对象
============

(1).面向对象和结构化程序设计的特点:
1>.结构化程序设计
程序 = 算法 + 数据结构
算法第一,数据结构第二

2>.面向对象程序设计OOP
程序 = 对象 + 对象 + ....
关键:让每一个对象负责执行一组相关任务

3>.面向对象编程开发范式的特点
万物皆对象
程序是一组对象彼此之间在发送消息
每个对象都有自己的内存占用,可以组成更大的对象
每个对象都有类型,特定类型的所有对象可以接收相同消息

----------------------------------
(2).概念:
[类]:
    类是创建对象的模板和蓝图
    类是一组类似对象的共同抽象定义

[对象]:
    对象是类的实例化结果
    对象是实实在在的存在,代表现实世界的某一事物,对象是一个类的实例化个体

[对象的三大关键特性]:
    [行为]:对象能干什么
    [状态]: 对象的属性,行为的结果
    [标识]: 对象的唯一身份

[类和对象的区别]:
    类是静态定义
    对象是动态实例
    程序代码操作的是对象而非类
    建模得到的是类而非对象

[类和对象的联系]:
    类是对象的定义
    对象的产生离不开类这个模板
    类存在的目的是实例化得到的对象


(3).类的关键字class:
-----------------
class 类名称{

public:
    //共有函数

protected:
    //保护成员

private:
    //私有成员
    //私有数据成员
    int val;
};    //注意类结尾处的分号


(4).类的定义:数据成员一般都是私有的,可以通过共有的方法去访问

[在类中定义成员函数]:
    类中定义的成员函数一般为内联函数,即使没有明确用inline标示
    在c++中,类定义通常在头文件中因此这些成员函数定义也伴随着进入头文件

[在类之后定义成员函数]:
    c++允许在其他地方定义成员函数
    将类定义和其成员函数定义分开,是目前开发程序的通常做法
    我们把类定义(头文件)看成是类的外部接口,类的成员函数定义看成是类的内部实现


(5).成员函数的调用:通过点的方式来调用
int main()
{
    Tdate a;
    a.set(2, 4, 6);
    a.print();
}

注:类中的方法不占用对象的大小,只有成员数据才占用对象的大小 sizeof(a),大小是\
    类中的成员变量的总大小

(6).函数的默认参数和函数重载在类中一样适用,对于不同类中的方法名字相同但是不算\
    函数重载


(7).类外定义,头文件中定义,.cpp中类外定义方法,main.cpp中声明对象使用
    代码在 code/5_oop/car/ 下

(8).this指针
    this指针代表当前对象占用的内存空间
    可以用指针和引用来调用成员函数


int main(int argc, char *argv[])
{
    Car mycar;
    mycar.CarRun();
    mycar.CarStop();
    mycar.setPro(10000, 10109);

    cout<<sizeof(mycar)<<endl;
    cout<<"mycar addr: "<<&mycar<<endl;
    mycar.print();

    //用引用来调用成员函数
    Car &car = mycar;

    car.CarRun();

    //用指针来调用成员函数
    Car *ptr = &mycar;
    ptr->CarStop();

    return 0;
}



2.封裝
===========
(1).概念
    类背后隐藏的思想是数据抽象和封装
    信息隐藏,隐藏对象的实现细节,不让外部直接访问到
    
    [封装]:将数据成员和成员函数包装进类中,加上具体实现的隐藏共同被称为封装,\
            其结果是一个同时带有特征和行为的数据类型

    封装就是将数据成员和方法进行隐藏和保护,private进行保护,留出接口实现操作
    封装就是让客户端程序员无法接触他们不应该接触的细节,进行细节的隐藏



3.构造函数和析构函数
=======================
(1).对象在定义的时候进行初始化
    1>.完成对象的初始化的函数是构造函数
    2>.类的对象初始化只能由类的成员函数来进行
    3>.建立对象的同时,自动调用构造函数
    4>.类对象的定义涉及到一个类名和一个对象名
    5>.由于类的唯一性和对象的多样性,用类名而不是对象名作为构造函数名是比较合\
    适的
    6>.默认构造函数:c++规定每一个类必须有一个构造函数,没有定义但是有一个默认\
    的构造函数
    
    [注]:1>.只要一个类定义一个构造函数,c++就不再使用默认的构造函数
         2>.与变量定义类似,在用默认的构造函数创建对象时,如果创建的是全局对\
         象或静态对象,则对象的位模式全为0,否则,对象值是随机的
    
(2).构造函数的定义(构造函数可以重载)
[构造函数]:构造函数无返回值 && 方法名与类名相同:

class Car{
public:
    name(与类名相同)(形参)      //可以无形参,形参可以有默认值
    {
        //构造函数的实现    
    }

private:
    int val;
    ...
};

int main(void)
{
    Car mycar(10000, 8888);        //在声明对象的时候传入构造函数的参数
    return 0;
}

(3).初始化列表方式的构造函数

#include <iostream>
using namespace std;


class Student{

    public:
        Student(int id, int score);
        void print(void);

    private:
        int m_id;
        int m_score;
};

//初始化列表的方式初始化变量,顺序要和变量定义的顺序一样
Student::Student(int id, int score)
:m_id(id), m_score(score)
{
}

void Student::print()
{
    cout<<"id = "<<m_id<<"\nscore = "<<" "<<m_score<<endl;
}

int main(void)
{
    Student s(1001, 100);
    s.print();

    return 0;
}



(4).禁止构造函数中进行隐式类型转换
    explicit Student();     //用explicit修饰构造函数禁止构造函数中进行隐式类型转换


(5).析构函数
1>.一个类可能在构造函数里分配资源,这些资源需要在对象不复存在以前被释放
2>.析构函数也是特殊类型的成员,它没有返回类型,没有参数,不能随意调用,也没有\
   重载,只是在类对象生命期结束的时候,由系统自动调用
3>.析构函数名,就在构造函数名前加上一个逻辑非运算符"~",表示"逆构造函数"
4>.在main函数退出时调用
5>.如果类没有定义析构函数,则编译器会有一个默认的析构函数


class Student{

    public:
        Student(int id, int score);
        ~Student();        //析构函数,无形参,无返回值,不能重载
        void print(void);

    private:
        int m_id;
        int m_score;
};



4.标准库类型string
======================
c++中的string不同于c中的字符串,c中的字符串是字符数组,而c++中的string\
是一个类里面有好多可以用的成员函数
string的定义为 typedef basic_string string;

常用的成员函数:

string s;
s.empty();    //如果字符串为空,返回true,否则返回flase
s.size();    //返回字符串中字符的个数
s1 + s2;    //将s1和s2连接成一个字符串,返回新生成的字符串
s1 = s2;    //将s1的内容替换成s2的内容
s1 == s2;    //比较s1和s2的内容,相等则返回true,否则返回flase



5.static类成员
===============
static修饰的成员函数和成员变量是属于类的级别的,所以不能使用this指针,\
this指针是对象的地址,可以用来替代全局变量



6.动态内存分配
===============
(1).malloc/free c语言

(2).  内存区域  
      +++++++++++++
      +              +    全局变量、静态数据、常量
      + data area +        
      +++++++++++++
      +              + 所有类成员函数和非成员函数代码
      + code area +
      +++++++++++++
      +              + 为运行函数而分配的局部变量、
      + stack area+ 函数参数、返回数据、返回地址等
      +++++++++++++
      +              + 动态内存分配区域
      + heap area +
      +++++++++++++

(3).c++的运算符new/delete
    在堆上生成对象,生成时会自动调用构造函数
    在堆上生成的对象,在释放时会自动调用析构函数
    new/delete, malloc/free需要配对使用
    new []/delete [] 生成和释放对象数组
    new/delete是运算符,malloc/free是函数调用

(4).Test *pVal = new Test();     //new一个新的对象
    delete pVal;        //释放内存空间
    pVal = NULL;        //防止内存泄漏,在释放后再被调用时会产生一个段错误

    Test *pArray = new Test[2]();     //new一个对象数组
    delete[] pArray;             //释放数组对象
    pArray = NULL;

[注]:new 完的对象一定要释放delete,不然会产生内存泄漏
     用new申请对象数组空间后一定要用delete[] 释放全部的数组对象,如果只\
     delete会不完全释放泄漏内存

(5).1>.栈中存储自动变量或者局部变量,以及传递的参数等
    2>.在一个函数内部定义了一个变量,或者向函数传递参数时,这些变量和参数存储在\
       在栈上,当变量退出这些变量的作用域时,这些栈上的存储单元会被自动释放
    3>.堆是用户程序控制的存储区,存储动态产生的数据
    4>.当用malloc/new来申请一块内存或者创建一个对象时,申请的内存在堆上分配,需\
       要记录得到的地址,用一个指针来接收地址,并且不需要的时候释放这些内存
    5>.栈一般很小,满足不了程序逻辑的要求
    6>.对象的生命周期是指对象从创建到被销毁的过程,创建对象时要被占用一定的内存\
       因此整个程序占用的内存随着对象的创建和销毁动态的发生变化
    7>.变量的作用域决定了对象的生命周期
    8>.全局对象在main之前被创建,main退出后被销毁
    9>.静态对象和全局对象类似,第一次进入作用域被创建,但是程序开始时,内存已经\
       分配好
    10>.作用域是由{}决定的,并不一定是整个函数
    11>.通过new创建对象,但容易造成内存泄漏,通过new创建的对象一直存在,直到\
         被delete销毁
    12>.隐藏在中间的临时变量的创建和销毁,生命周期很短,容易造成问题

(6).new/malloc 和 Test *pVal = &t的区别
    
    Test t();
    Test *pVal = &t;
    t是在栈空间分配内存的随着{}区域而被销毁,而Test *pVal = new Test();\
    是在堆上分配空间,直到被delete以后才会被释放


7.拷贝构造函数
===============
(1).1>.拷贝构造函数(copy constructor)是一种特殊的构造函数,具有单个形参,此行参\
    是对该类型的引用,当定义一个新对象并用一个类型的对象对它进行初始化时,将\
    显示使用拷贝构造函数
    2>.当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式的调用拷贝\
    构造函数
    3>.如果一个类没有定义拷贝构造函数,编译器会默认提供拷贝构造函数

(2).拷贝构造函数:
    我们在定义一个变量 int a = 100; int b = a; 的时候可以将a的值简单的传递给b
    但是我们定义一个对象 Student s1(); Student s2 = s1; 的时候s1需要将一些私有\
    成员变量的值赋值给s2这个时候就会用到拷贝构造函数,或者在函数传递对象变量\
    和函数返回对象时都需要复制出一个副本,这个时候就需要用到拷贝构造函

(3).拷贝构造函数以引用作为参数是因为引用和原类的地址一样相当于指针

(4).编译器提供的默认拷贝构造函数的行为
    1>.执行逐个成员初始化,将新对象初始化为原对象的副本
    2>.“逐个成员”,指的是编译器将现有对象的每个非static成员,依次复制到正在创建\    
    的新对象

(5).深拷贝与浅拷贝
    在某些情况下类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里\
    的值完全复制给另外一个对象,如A=B,这时,如果B中有一个成员变量指针已经申请\
    了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题,当B把内存释放\
    了(如:析构),这时A内的指针就是野指针,会出现运行错误。
    
    深拷贝和浅拷贝可以理解为:如果一个类拥有资源(锁,指针),当这个类的对象发生\
    复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,\
    就是浅拷贝,使用默认拷贝构造函数的过程中比如指针没有重新分配资源,两个指针\
    指向同一块空间,就是浅拷贝

------------------------------------

#include <iostream>

using namespace std;

class Student{

public:
    Student(int val)
    :m_val(val){
    }

    ~Student(){
    }

    //拷贝构造函数,无返回值,形参只有一个,是本类的引用
    Student(const Student &s)
    {
        cout<<"My is class Student of copy constructor!"<<endl;
        cout<<&s<<endl;
    }
    
    void print(void)
    {
        cout<<"m_val = "<<m_val<<endl;        
    }

private:
    int m_val;
};

void func1(Student stu)
{
    cout<<"函数传递参数时用到拷贝构造函数"<<endl;    
}

//函数传递返回值的时候用到拷贝构造函数
Student func2(void)
{
    cout<<"函数传递返回值时用到拷贝构造函数"<<endl;    
    Student stu(100);
    return stu;
}


int main(void)
{
    Student stu1(100);
    stu1.print();
    Student stu2 = stu1;
    cout<<&stu1<<endl;
    
    retuern 0;
}



8.const关键字
===============
1>.const限定符
   const int BUFFER_SIZE = 512;
   指定一个不该被改动的对象
2>.const限定指针类型
   const出现在星号左边,表示指向的变量是常量
   int const *ptr;    //指针常量
    
   const出现在星号右边,表示指针自身是常量,指向不能变
   int *const ptr;    //常量指针



9.友元函数与友元类
====================
c++函数特性

(1).在某些情况下,允许特定的非成员函数访问一个类的私有成员,同时仍然阻止一般\
    的访问
(2).友元(friend)机制允许一个类将对其非公有成员的访问权授予指定的函数或类
    1>.友元的声明以关键字friend开始
    2>.只能出现在类定义的内部
    3>.可以出现在类中的任何地方,不是授予友元关系的那个类成员,所以不受其声明\         出现部分的访问控制影响
(3).友元关系是授予的
    为了让B成为A类的友元,类A必须显示声明类B是他的友元
(4).友元关系是不对称的
    如果类A是类B的友元,类B是类C的友元,不能推出类B是类A的友元
(5).友元会破坏封装



10.使用valgrind检测内存泄漏
==============================
valgrind --tool=memcheck --show-reachable=yes --leak-check=yes ./a.out(可执行文件)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值