C++学习笔记(一)

看过一轮黑马了,重新看看侯捷老师的,记录一下不会的/忘了的

两大类:

  1. 基于对象Object Based
  2. 面向对象Object Oriented

基于对象

不含指针的class -- 设计一个复数类

Class without pointer member(s) -- complex

Header中的防伪式声明:
        若曾经没有定义过该内容,则在此处定义:(以复数complex为例)

//complex.h

#ifndef _COMPLEX_
#define _COMPLEX_

//...

#endif

class template(模版)简介

inline函数 -- 解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题

        关键字必须与函数定义放在一起才能使函数成为内联函数【inline complex& _doapl(complex* ths, const complex& r){...}】,仅仅将inline放在函数声明前面不起任何作用

        inline是一种“用于实现”的关键字,而不是一种“用于声明”的关键字。

access level(访问级别):public,private,protected

constructor(ctor,构造函数)-- 可以有很多个重载overloading

        在初始化的时候给构造函数的值进行初始化:(更快)

complex (double r = 0, double i = 0)
    : re (r), im (i) //initialization list(初值化 初始列)
{  }

const member function(常量成员函数)

class complex{
public:
    double real () const {return re};
    double imag () const {return im};
    //对于只读操作的函数,在函数中 + const
private:
    double re, im;
}

参数传递: pass by value VS pass by reference(to const) 

                尽量传引用(ostream& ),若不想改变量值,则+const(const complex&);

friend(友元)-- 相同class的各个objects互为friends 

class body 外的各种定义 

operator overloading (操作符重载)--成员函数

        1.参数有两个数时用this指针?(1.和2.的参数怎么设置)

                任何成员函数都有一个隐藏的this指针,谁调用这个函数this就指向谁 

        2.return by reference 语法分析

                该例中有 conplex& 的引用返回,发送者无需知道接受者是以reference形式接受

                用return by reference对连续赋值有好处

//e.g 对复数进行加法的操作符重载
{
    complex c1(2,1);//定义了一个复数c1,实部为2 虚部为1
    complex c2(5);//定义了一个复数c2,实部为5 虚部为0

    c2 += c1;//重载+=操作符
}

//do assignment plus
inline complex _doapl(complex* ths, const complex& r)
//完成两个复数相加操作,返回结果
{
    ths->re += r.re;
    the->im += r.im;
    return *ths;//虽然返回的是一个指针,但是传递者无需知道接受者是以reference形式接收
}

//1. = 2.
inline complex& complex::oprator += (const complex& r)
//重载+=,参数中有一个隐藏参数
{
    return _doapl(this, r);
}
//2. = 1.
inline complex& complex::operator += (this, const complex& r)
{
    return _doap1 (this, r);
}

temp object(临时对象) typename();

        没有名称,生命很短暂  

        绝对不能return by reference,因为返回的必定是个local object(局部变量)

ostream& os -- 每一次cout都有改变,所以在作为参数时不能加const

含指针的class -- 设计一个字符串类

Class with pointer member(s) -- string

class String
{
public:
    String(const char* cstr = 0);//默认构造
    //Big Three,三个特殊函数
    String(const String& str);//1.拷贝构造
    String& operator=(const String& str);//操作符重载,2.拷贝赋值
    ~String();//析构函数:若类带有指针,一定要写3.析构函数
    char* get_c_str() const {
        return m_data;
    }
private:
    char* m_data;//用指针就可以让字符串长度具有灵活性
};

ctor 和 dtor (构造函数和析构函数)

//默认拷贝构造,赋初值
inline String::String(const char* cstr = 0)
{
    if (cstr) 
    {
        m_data = new char[strlen(cstr)+1];//动态分配内存
        strcopy(m_data, cstr);
    }
    else
    {
        m_data = new char[1];//动态分配内存
        *m_data = '\0';
    }
}

inline String::~String()
{
    delete[] m_data;//清理内存,防止内存泄漏
}


//test函数
{
    String s1();
    String s2("hello");

    String* p = new String("hello");//动态分配内存
    delete p;//清理内存,防止内存泄漏
}

class with pointer members必须有copy ctor (拷贝构造)和 copy op=(拷贝赋值)

//拷贝构造--深拷贝
inline String::String(const String& str)
{
    m_data = new char[ strlen(str.m_data) + 1 ];
    strcpy(m_data, str.m_data);
    //直接取另一个object的private(兄弟之间互为friend)
}

//copy assignment operator -- 拷贝赋值函数
inline String& String::operator=(const String& str)
{
    //检测自我赋值self assignment
    if (this == &str)
        return *this;//不仅效率更高,而且不会产生不确定行为(保证正确性)

    delete[] m_data;
    m_data = new char[ strlen(str.m_data) + 1 ];
    strcpy(m_data, str.m_data);
    return *this;
}

//test
{
    String s1("hello");
    String s2(s1);    //拷贝构造,等同于String s2 = s1;
    s2 = s1;    //拷贝赋值构造
}

 output函数

        cout一定要设置为全局函数

#include <iostream.h>
//操作符重载 输出字符串 
ostream& operator<<(ostream& os, const String& str)
{
    os << str.get_c_str();
    return os;
}

//test
{
    String s1("hello");
    cout << s1;
}

栈stack,堆heap

        全局对象global object--其生命在整个程序结束之后才结束

        静态对象static object--在作用域scope结束之后仍然存在,直到整个程序结束(在整个程序结束后才调用析构函数)

        创建对象新方式“new”--动态分配内存--先创建空间,再调用ctor构造函数

Complex* pc = new Complex(1,2);

//编译器将转化为如下
Complex *pc;
void* men = operator new( sizeof(Complex) );//分配内存
//其内部会调用malloc(n)
pc = static_cast<Complex*>(mem);//转型
pc->Complex::Complex(1,2);//构造函数,全名为Complex::Complex(pc,1,2);//pc为Complex的起始位置

        delete--先调用dtor析构函数,后释放内存        

String* ps = new String("hello");
...
delete ps;

//编译器将转化为如下:
String::~String(ps);//析构函数
operator delete(ps);//释放内存,其内部调用free(ps)

 动态分配所得到的内存块memory block,in VC

        array new 一定要搭配array delete

delete[] p; //delete后面需要有数组标志告诉计算机要delete的是一个数组

字符串本身是4byte

基于对象的一些细节补充:

static

        可以修饰数据,也可以修饰函数

static data members -- 静态数据会单独分配一个内存,且只有一份

static member function -- 静态函数没有this指针,只能存取静态数据

static需要在class外写一个定义:

//e.g 一个银行账户类
class Account{
    static double m_rate;
    static void set_rate(const double& x){
        m_rate = x;
    }
};
double Account::m_rate = 8.0;//外部要对类内的静态变量做一个定义
//类型 变量全名 初值(选)

//静态函数调用的两种方式
int main(){
    Account::set_rate(5.0);//1.通过class name调用

    Account a;
    a.set_rate(7.0);//2.通过object调用
    //因为是静态函数,所以不会把对象a的地址放入
}

静态函数的调用方式:1.通过对象调用;2.通过类名调用

单例模式中的静态变量

        把ctor放在private中

//Singleton
class A{
public:
    //用一个静态函数 去得到a
    static A& getInstance{
        return a;
    }
    setup(){...}
private:
    A();
    A(const A& rhs);
    static A a;//a只有一份它自己
    ...
};
//调用函数:
A::getInstance().setup();
//用getInstance取得a,再调用setup函数




//Singleton优化:在没有使用时不先创建a
//Meyers Singleton
class A{
public:
    static A& getInstance();
    setup(){...}
private:
    A();
    A(const A& rhs);
    ...
};

A& A::getInstance(){
    static A a; //静态的a放在函数中
    return a;
}
//调用函数:
A::getInstance().setup();

cout

        ostream类型

template,模版

        类模版

//class template
template<typename T>
class complex{...};

//使用
complex<double> c1(2.5, 1.5);
complex<int> c2(2, 1);

        函数模版

//function template
//函数功能:比大小
template <class T>
inline const T& min(const T& a, const T& b){
    return b < a ? b : a;
}

//编译器会对function template进行引数推导argument deduction(实参推导)
//函数调用
stone r1(2, 3), r2(3, 3), r3;
r3 = min(r1, r2);

namespace

//语法
namespace std
{
    ...//std是一个标准库
}
//被包装在std这个命名空间

命名空间 的打开方式

//1.using directive直接打开
#include <iostream.h>
using namespace std;

int main(){
    cin << ...;
    cout << ...;
    return 0;
}


//2.using declaration使用声明
#include<iostream.h>
using std::cout;

int main(){
    std::cin << ...;
    cout << ...;
    return 0;
}


//3.不用using
#include <iostream.h>
int main(){
    std::cin << ...;
    std::cout << ...;
    return 0;
}

面向对象

Object Oriented Programming, Object Oriented Design -- OOP, OOD

Inheritance,继承

Composition,复合

Delegation,委托

复合Composition,表示has-a

//Adapter -- 适配器
template <class T, class Sequence = deque<T> >
class queue {
    ...
protected:
    Sequence c; //底层容器,类似于 deque<T> c;
public:
    //以下完全利用deque c的操作函数完成,queue借用了deque类中的功能
    bool empty() const {
        return c.empty();
    }
    size_type size() const {
        return c.size();
    }
    reference front() {
        return c.front();
    }
    reference back() {
        return c.back();
    }
    //deque是两端可进出,queue是先进先出(末端进前端出)
    void push(const value_type& x) {
        c.push_back(x);
    }
    void pop() {
        c.pop_front();//用deque中前端出的功能用作queue的调出功能
    }
};

Composition关系下的构造和析构

        Container object 包含于 Component part

        构造由内而外:Container的构造函数首先调用Component的default构造函数,然后才执行自己

Container::Container(...): Component() { ... };

         析构函数由外而内:Container的析构函数首先执行自己,然后才调用Component的析构函数

Container::~Container(...){ ... ~Component() };

Delegation(委托) -- Composition by reference

        reference counting引用计数 -- 共享相同的内容

// file String.hpp
class StringRep;
class String {
public:
    String();
    String(const char* s);
    String(const String& s);
    String &operator=(const String& s);
    ~String();
...
private:
    StringPep* rep; //pimpl -- Pointer toimplementation
}

//n个字符串可以共享同一份
//👆Handle 👇Body  --  plmpl


// file String.cpp
#include "String.hpp"
namespace{
class StringRep {
friend class String;
    StringRep(const char* s);
    ~StringRep();
    int count;
    char* rep;
};
}

String::String(){ ... }
...

Pimpl(Pointer to implementation) 是一种减少代码依赖和编译时间的C++编程技巧,其基本思想是将一个外部可见类(visible class)的实现细节(一般是所有私有的非虚成员)放在一个单独的实现类(implementation class)中,而在可见类中通过一个私有指针来间接访问该实现类。                                                                                   --知乎,C++编程技巧: Pimpl

Inheritance(继承),表示is-a

        构造由内而外(先父后子),析构由外而内(先子后父)

        base class的dtor必须是virtual,否则会出现undefined behavior

inheritance with virtual function   

        函数继承 -- 继承权

        成员函数 1.非虚函数;2.虚函数(希望子类重新定义override且有默认定义);3.纯虚函数(希望子类一定要重新定义,你对它没有默认定义)

class Shape{
public:
    virtual void draw() const = 0;//pure virtual
    virtual void error(const std::string& msg);//impure virtual
    int objectID() const;//non-virtual
    ...
};

class Rectangle: public Shape{...};
class Ellipse: public Shape{...};

  ...

#include <iostream>
using namespace std;

//Base类
class CDocument{
public:
    void OnFileOpen(){
    //这是个算法,每个cout输出代表一个实际动作
    cout << "dialog..." << endl;
    cout << "check file status..." << endl;
    cout << "open file..." << endl;
    Serialze();//加载
    cout << "close file..." << endl;
    cout << "update all views..." << endl;
    }

    virtual void Serialize() { };
};
//Derive类
class CMyDoc : public CDocument{
public:
    virtual void Serialize(){
        //只有应用程序本身才知道如何读取自己的文件
        cout << "CMyDoc::Serialize()" << endl;
    }
};
int main(){
    CMyDoc myDoc;
    myDoc.OnFileOpen();//通过子类对象调用父类函数
}

Inheritance + Composition

Delegation + Inheritance --三大关系中功能最强大的一种组合

        观察者Observer

class Observer{
public:
    virtual void update(Subject* sub, int value) = 0;
}
//一个subject有很多observer
class Subject{
    int m_value;
    vector<Observer*> m_views;//使用容器放指针指向observer
public:
    //将数据放入
    void attach(Observer* obs){
        m_views.push_back(obs);
    }
    void sct_val(int value){
        m_value = value;
        notify();
    }
    //遍历通知
    void notify(){
        for(int i = 0; i < m_views.size(); ++i)
            m_view[i] -> update(this, m_value);//更新数据
    }
};

        组合模式Composite

//Base类
class Component{
    int value;
public:
    Component(int val){
        value = val;
    }
    virtual void add(Component*){ }
};



class Composite: public Component{
    vector<Component*> c;
public:
    Composite(int val):Component(val){ }
    
    //参数 是 指向父类的指针
    void add(Component* elem){
        c.push_back(elem);
    }
...
};


//代表flie的class,基本的
class Primitive: public Component{ 
public:
    Primitive(int val):Component(val){}
};

        Prototype原型模式 
                加下划线 代表是 一个静态对象;
                “-”(负) 代表 私有的private
                “#” 代表 protected




源代码:

//Base类
#include <iostream.h>
enum imageType{
    LAST,SPOT
};

class Image{
public:
    virtual void draw() = 0;
    static Image *findAndClone(imageType);
protected:
    virtual imageType returnType() = 0;
    virtual Image *clone() = 0;

    static void addPrototype(Image *image){
        _prototype[_nextSlot++] = image;
    }
private:
    static Image *_prototype[10];
    static int _nextSlot;
};
//给类内的静态变量/函数去定义
Image *Image::_prototypes[];
int Image::_nextSlot;

//Client calls this public static member function when it needs instance of an Image subclass
Image *Image::findAndClone(imageType type){
    for(int i = 0; i < _nextSlot; i++)
        if(_prototype[i]->returnType() == type)
            return _prototypes[i]->clone();
}
//Derive类--LSAT
class LandSatImage:public Image{
public:
    imageType returnType(){
        return LSAT;
    }
    void draw(){
        cout << "LandSatImage" << _id << endl;
    }
    //When clone() is called, call the one-argument ctor with a dummy arg
    Image *clone(){
        return new LandSatImage(1); //new一个自己
    }
protected:
    //This is only called from clone()
    LandSatImage(int dummy){
        _id = _count++;
    }
private:
    //Mechanism for initializing an Image subclass - this cause the default ctor to be called, which registers the subclass's prototype
    static LandSatImage _landSatImage;
    //this is only called when the private static data member is inited 
    LandSatImage(){
        addPrototype(this);
    }
    //Nominal "state"per instance mechanisim
    int _id;
    static int _count;
};
//Register the subclass's prototype
LandSatImage LandSatImage::_LandSatImage;
//Initialize the "state" per instance mechanism
int LandSatImage::_count = 1;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值