侯捷C++-1 & 2

侯捷老师经典 C++

3.3 构造函数

  • 访问级别
  • 构造函数——没有返回值类型
  • 大气的正规的写法:利用构造函数的初始化列表,初始化时第一阶段对变量的赋值,时间和效率上都有所提高
  • 构造函数可以有很多个——overloading(重载)

4.4 参数传递和返回值

  • 把构造函数放到 private 里面——需要在public 中调用singleton单例模式

  • 常成员函数——在函数的后面加上const,花括号的前面,class里面的操作有会改变数据和不会改变数据两种,

    //complex类的一个成员函数,public 权限
    double real() const
    {retrun re;}
    

    表示不会改,只是拿这个数据,要加就一定要加,有时候不加是会出事情的

    //加入在类外定义一个常对象,使用者说我这个c1是不能变得哦
    const complex c1(2,1);
    cout << c1.real() <<endl;
    

    这样,如果之前成员函数的定义没有写 const 会导致编译器说不行。

  • 传值(pass by value)还是 传引用(pass by reference)——再过去的C语言可以传指针,但是C++有引用,那么的快,形式又漂亮。最好所有的参数都传引用,如果我传的时候不想你去改我的内容(因为人家毕竟是双向传递),我就可以设成传递常引用

    complex& oprerator += (const complex& );
    

    你只要一改,我就出错。

  • 返回值(return by value)还是返回引用(return by reference)(影响效率的细节)

  • 友元(friend)朋友打破了封装的大门

  • 多年的程序员可能也会提起(知道这样写但不知道为什么)——相同 class 的各个objects互为友元

  • 老司机看类body代码(数据在 private、传引用(要不要加const看情况)、返回值尽量用reference、在类的中能加const的地方就要加,如果不加,使用者在用的时候报错编译器会埋怨你、构造函数的 initialize list尽量用)

  • inline complex&
        __doap1(complex* ths,const complex& r)
    {
        ths->re +=r.re;           
        ths->im += r.im;
        return *ths;
    }
    
    inline complex&
        complex::operator += (const complex& r)
    {
        return __doap1(this,r);
    }
    

    在函数里面保存一个结果时有两种,第一种是保存在一个传入的变量中,第二种是建立一个变量(但是这个变量是local 变量)保存,但是第二种不能返回引用,函数用完它的本体都死亡了,你再传出去,回来看这个空间只会看到一些坏的东西不好的东西。

5.5 操作符重载与临时对象

  • head 头文件中的防卫式说明

  • 操作符重载定义在成员函数,对于双目运算符的重载在参数表中只能写一个参数,第一个this省略但不能写。

  • 操作符重载在非成员函数,双目运算符的形参列表要写两个参数。——全局函数

  • return by reference 语法分析——传递者无需知道接受者是以reference形式接收。

  • class body之外的各种定义

  • temp object 临时对象

    inline complex
    operator + (const complex& x, const complex& y)
    {
        return complex(real(x) + real(y),
                       imag(x) + imag(y));
    }
    
    inline complex
    operator + (const complex& x, double y)
    {
        return complex(real(x) + y, imag(x));
    }
    
    inline complex
    operator + (double x, const complex& y)
    {
        return complex(x + real(y),imag(y));
    }
    
  • 对 “ << ” 的重载,标准库的对象作为参数时,只能写成非成员函数的形式。在这里不可以加const ,因为在这里每一次输出都在改这个对象的状态

    #include <iostream.h>
    ostream&
        operator << ( ostream& os,const complex& x)
    {  
        return os << "(" << real(x) << "," << imag(x) << ")";
    }
    

6.6 复习complex 类的实现过程

//complex.h
#ifndef __COMPLEX__
#define __COMPLEX__

class complex
{
    public:
    complex(double r = 0,double i = 0): re (r), im (i) 
    { }
    complex& operator += (const complex&);
    double real() const {return re}
    double imag() const {return im}
	
    private:
    double re,im;
    friend complex& __doap1(complex*,const complex&);
};
#endif

inline complex&
__doap1(complex* ths,const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;
}

inline complex&
complex::operator +=(const complex& r)
{
    return __doap1(this,r);
}

inline complex
operator + (const complex& x,const complex& y)
{
    return complex (real(x) + real(y),
                    imag(x) + imag(y));
}

inline complex
operator + (const complex& x,double y)
{
    return(real(x)+y,imag(x));
}

inline complex
operator + (double x,const complex& y)
{
    return (x+real(y),imag(y));
}

#include<iostream.h>
ostream& 
    operator << (otream& os,const complex& x)
{
    return os << "(" << x.real() << "," <<x.imag() << ")" ;
}

7.7 三大函数:拷贝构造、拷贝赋值、析构

  • 浅拷贝与深拷贝
  • 拷贝赋值,先杀掉自己,再给自己分配空间,数据的赋值,返回*this,这个过程里先检测是否自我赋值

8.8 栈与内存管理

  • output 函数

  • 所谓的 stack(栈),所谓的 heap(堆),堆是由操作系统提供的一块global的内存空间

  • stack objects 的生命期,static object 的生命在scope结束后依然存在,知道程序结束。

  • global objects 的生命期

  • 内存泄漏——动态分配的内存的指针死亡了,但是那块动态分配的内存仍然存在,所以造成内存泄漏

  • new的过程:先调用 malloc 分配内存,再调用构造函数,它的反作用是delete ,delete过程是首先调用析构函数,再释放(free())内存

  • 动态分配所得的内存块(memory block),in VC,array new如果不搭配 array delete 来使用的话,会导致原本需要调用n次析构函数只调用了一次,内存泄漏的是没有调用析构的地方(与类中有无动态分配的内存还有关系)。万无一失的做法就是只要有array new 就要有 array delete

9.9 复习 string 类的实现过程

Class String
{
public:
	String(const char* cstr = 0);
	String(const String& str);
	String& operator = (const String& str);
	~String();
	char* get_c_str()const
	{
	return m_data;
	}
private:
	char* m_data;
}

inline
String::String(const char* cstr = 0)
{
    if(cstr)
    {
        m_data = new char[strlen(cstr) + 1];
        strcpy(m_data, cstr);
    }
    else
    {
        m_data = new char[1];
        *m_data = '\0';
    }
}

//析构函数
inline
String::~String()
{
    delete[] m_data;
}

inline
String::String(const String& str)
{
    m_data = new char[ strlen(str.m_data) + 1];
    strcpy(m_data,str.m_data);
}

//拷贝赋值函数
inline
String& String::operator=(const String& str)
{
    //自我赋值检测
    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;
}


10.10 扩展补充:类模板、函数模板及其他

  • static ——在一个class里面使用 static ,它存在一个单独的区域,郑莉之前说哦过:属于类的,不是属于某一个对象,static 函数处理static 数据,如下代码所示:调用静态函数有两种方式(1)通过object调用,(2)通过class name调用,之前郑莉说尽量用class name 来调用

    class Account
    {
    public:
        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);
        
        Account a;
        a.set_rate(7.0);
    }
    
  • 进一步补充,将构造函数放在 private 里面,在这一种单例设计模式(Singleton)

    //Singleton
    class A
    {
    public:
        static A& getInstance(return a;);
        setup(){...}
    private:
        A();
        A(const A& rhs);
        static A a;
        ...
    };
    
  • cout 的重载补充介绍(简略)

  • class template 类模板,有人说模板会造成代码的膨胀,但是这种膨胀是必要的,function template ,函数模板,它可以不用进行T 的指定,因为编译器会对 function template 进行引数推导

  • 进一步补充:namespace ,在使用命名空间几种用法

    1. using namespace std
    2. using std::cout
    3. std::cout <<…

11.11 组合与继承

  • Composition(复合),我中有你,要构造这个对象,先把里面的对象构造好,构造由内而外,析构由外而内。郑莉之前有实例说明(书上)
  • Delegation(委托),有一个指针指向其他的类,也有个说法叫 Composition by reference,不同步,这种写法现在非常有名叫做 pimpl(指针应用实现)也是Handle/Body(pimpl)。这种手法又叫编译防火墙。
  • Inheritance(继承),最有价值的一点就是核虚函数搭配,构造的时候由内而外,析构由外而内。一个良好的编程习惯,只要你的class 马上要变成一个父类,或者将来会可能变成父类,就把你的析构函数设置成 virtual

12.12 虚函数与多态

  • Inheritance(继承) with virtual function(虚函数),继承中函数的继承是继承调用权,成员函数可以分为(1)non-virtual 函数:你不希望 derived class 重新定义(override,覆写),(2)virtual函数:你希望derived class 重新定义它(3)pure virtual函数(纯虚函数):你希望 derived class 一定要重新定义,你对他没有默认
  • 通过子类的对象调用父类的函数,如果在这个父类的函数中含有子类所定义的虚函数,那么会去之类执行那个虚函数再回父类继续执行之前的父类函数,最后返回,这种做法如此的经典以至于有个专门的名称叫做 Tem-plate Method(大名鼎鼎的 23 个设计模式之一)——虚函数里面的经典用法
  • Inheritance + composition 关系下的构造和析构,子类的对象里面既包含父类的东西有包含组合类的东西,它的构造过程谁先?
  • Delegation(委托) + Inheritance(继承),最强大

13.13 委托相关设计

  • 23个设计模式中的 Composite 模式

  • Prototype模式

更多细节需要探讨兼谈对象模型(新手们,享受下部)革命尚未成功,同志仍需努力

1.1 导读

2.2 conversion function ,转换函数

  • 一个类的对象可不可以转成其他类型,即转出去的问题

    class Fraction
    {
    public:
        Fraction(int num, int den=1): m_numerator(num),m_denominator(den){}
        //转换函数
        operator double() const
        {
            return (double)(m_numerator / m_denominator);
        }
    private:
        int m_numerator;					//分子
        int m_denominator;					//分母
    };
    
    Fraction f(3,5);
    double d = 4 + f;		//调用operator double()将 f 转为0.6
    

3.3 non-explicit-one-argument ctor

  • 对于含有 explicit 相当于告诉编译器不要那么的自动去转化,这种关键字用的地方比较少,一般在构造函数照片那个

    //这里的bool是模板的偏特化
    class Fraction
    {
    public:
    	explicit Fraction (int num,int den =1):m_numerator(num),m_denominator(den){}
        operator double() const
        {
            return (double) (m_numerator / m_deniminator);
        }
        Fracyion operator+(const Fraction& f)
        {
            retrun Fraction(...);
        }
    private:
        int m_numerator;
        int m_denominator;
    }
    
    Fraction f(5,3);
    Fraction d2 = f + 4;//[error]conversion from 'double' to'Fraction' 
    
  • 他在模板上的一个例子如下(转换函数的转进来):

    template<class Alloc>
    class vector<bool, Alloc>
    {
    public:
    	typedef __bit_reference reference;
    protected:
    	reference operator[] (size_type n)
    	{
    	return *(begin() + difference_type(n));
    	}
    };
    //其中必然有一个转换函数将reference 转换为 bool
    其中:
    struct __bit_reference
    {
        unsigned int* p;
        unsigned int mask;
    public:
        operator bool() const
        {return !(!(*p & mask))};
    }
    

4.4 point-like classes,关于智能指针

  • 通俗的说就是这个类所产生的对象像一个指针,把它叫做智能指针,智能指针要包含一般指针的作用

    template<class T>
    class shared_ptr
    {
    public:
    	T& operator*() const
        { return *px; }
        
        T& operator->() const
        { return px; }
        shared_ptr(T* p) : px(p){}
    private:
        T* px;
        long* pn;
    };
    
  • 迭代器,就是主要用来遍历容器的作用——也是智能指针

5.5 function-like classes,所谓仿函数

  • 类模仿函数,函数对象
  • 告诉各位,标准库里面有好多仿函数,这些仿函数都有继承一些奇怪的父类。继承这些有什么用,在C++标准库课程才会提到它。

6.6 namespace 经验谈

  • namespace 防止冲突
  • 测试的时候可以设置自己的namespace

7.7 class template,类模板

8.8 function template, 函数模板

9.9 Member template, 成员模板

  • 模板里面的模板
class Base1{};
class Derived1:public Base1{ };

class Base2{};
class Derived2:public Base2{ };

pair<Derived1, Derived2> p;
pair<Bae1, Base2> p2(p);

pair<Base1, Base2> p2(pair<Derived1, Derived2>());
  • 所以模板主要分为三大类:class、function、member

  • up cast(向上转型) 指的是对于一个指向基类的指针,但是它指向一个它的子类的对象时,那么这样就称之为 up-cast

10.10 specialization,模板特化

  • 特化与泛化是相对应的

11.11 partial specialization,模板偏特化

  • 个数上的偏
  • 范围上的偏——指向任何类型的指针

12.12 template template parameter,模板模板参数

  • 其实已经很少文档在讲这个东西,这已经很高深了,但是很简单务必搞明白的哦

  • 模板两个 typename ,其中有一个又是一个模板

    template <typename T,
    		  template<typename T>
    		       class Container
    		  >
    class XCLs{...};
    
    XCLs<string , list> mylst1;//这么写是不行的,因为容器需要好几个模板参数等,但是这一行上面的语法是没有问题的(非战之罪)
    

13.13 关于C++标准库

  • 建议去用标准库,不用自己去写那些功能,没人家写得好
  • 下一节开始就是选一些C++11的几个点来讲

14.14 三个主题

  • variadic templates(since C++11) 意味模板参数可变化,也表示的是数量不定的模板参数

    void print()
    { }
    
    template<typename T,typename... Types>
    void print(const T& firstArg,const Types&... args)
    {
        cout << firstArg <<endl;
        print(args...);				//递归
    }
    
  • auto——一个语法糖哈哈,使用 auto 的时候一定要编译器能帮你推出来,有人可能会说:全部用auto好了,这样就极端了

    //以前要这么写
    list<string> c;
    ...
    list<string>::iterator ite;
    ite = find(c.begin(),c.end(),target);
    
    //现在
    list<string> c;
    ...
    auto ite = find(c.begin(),c.end(),target);
    
  • range-base for (since C++11),将coll容器里面的元素给decl里面指向statement

for (decl : coll)
{
	statement;
}

//比如
for (int i :{1,2,3,5,6,7})
{
    cout << i << endl;
}

//再如
vector<double> vec;
...
for(auto elem :vec)
{
    cout << elem << endl;
}
for(auto& elem :vec)
{
    elem *= 3;
}

15.15 reference

  • 从内存的角度来探讨值(value)、指针、和引用(reference)
  • 引用其实里面有一个隐含的指针,设完就不可以变了,指针是 4 个字节,假象:引用取决于它代表的那个值是多大,他就多大(即object 和其 reference 的大小相同,地址也相同,全是假象)
  • reference 实际上是一个漂亮的 point
  • 两个相同的函数签名是不允许的(会导致二义性)

16.16 复合&继承关系下的构造和析构

17.17 对象模型(Object Model) vptr 和 vtbl

  • 父类有虚函数,子类一定有

  • 虚指针、虚表、子类覆写父类的函数、虚函数的调用——动态绑定、虚机制

    动态绑定的三个条件:

    1. 指针调用
    2. up cast
    3. 虚函数
  • 虚函数的调用的这种用法,我们叫做多态

18.18 对象模型(Object Model)关于 this

19.19 const

  • const,对于放在成员函数中时,意味着不打算改变这个class 的 data,这张表值得推敲一哈子

    const objectnon-const object
    const member functions (保证不更改 data members)yesyes
    non-const member functions(不保证 data members 不变)noyes

    当成员函数的 const 和 non-const 版本同时存在,const object 只会(只能)调用 const 版本,non-const object 只会(只能)调用 non-const 版本

20.20 对象模型(Object Model)关于 Dynamic Binding

  • 主要还是对于动态绑定的过程进行汇编码的解释

  • 这种动态绑定用 C 语言的机制描述是

    (*(p -> vptr)[n])(p);或
    (* p -> vptr[n])(p);
    
  • 这就是对这种对象模型的虚函数的很棒的表现的解释

21.21 关于new 和 delete

22.23 重载 operator new 、operator delete、operator new[]、operator delete[] & 示例(分配释放过程演示)

24.24 重载 new、delete

  • placement new : 我们可以重载 class member operator new(),写出多个版本,前提是每一个版本的声明都必须有独特的参数列,其中,第一个参数必须是size_t,其余参数以 new 所指定的placement arguments 为初值。

  • new

    1. 一般的 new
    2. operator new
    3. array new
    4. placement new
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值