【C++】01-C++面向对象高级编程(上)-笔记(侯捷系列)

1、在设计一个类的时候,数据应该尽量是private的,几乎没有例外。

2、构造函数应该用冒号后面初值列的形式,不应该在构造函数的函数体里面去赋值。

例如,

A::A(int a) : m(a)
{
    // 不应该用 m = a; 这种形式
} 

3、在设计一个函数的时候,如果这个函数不会改变数据,应该将这个函数修饰为const。

例如,double real() const { return re; }

假如不这么做,当一个用const修饰的对象去调用这个函数的时候,万一这个函数有可能会修改数据,那就矛盾了。因此,这种情况是编译不通过的。

4、参数传递的值传递与引用传递。

  • 值传递是整个值压栈,因此尽量不要使用值传递。
  • 引用传递的底层就是指针的传递,因此传递地非常快。
  • 建议是所有的参数传递都是引用传递。
  • 如果函数设计的时候不希望函数里面对某个实参进行修改,那么应该修改该形参为const。

5、返回值也会有值传递与引用传递。

  • 返回值的传递也尽量用引用传递,如果可以的话。
  • 结果是局部变量(在函数体内创建的局部变量)的时候,是不能用引用返回的,因为离开了作用域,该局部变量会销毁掉。

6、把一个函数设置为自己的friend,则该函数可以访问自己的私有成员。

friend会打破封装的大门。

7、相同class的各个objects互为friend。

8、传递者无需知道接收者是以引用形式接收的。

这个是引用传递优于指针传递的原因之一。

例如,

inline complex& complex::operator+=(const complex& r)
{
    // ...
}

9、临时对象的生命周期仅仅到下一行。

例如,

Complex();
Complex(1, 2);

10、C++的操作符都是作用于左侧对象的。

11、一种函数有两种写法,一种是写成成员函数,另一种是写成非成员函数(即全局函数)。

输出运算符重载不可以写成成员函数。因为,

习惯于写 cout << c1;

不习惯于写 c1 << cout;

12、 函数该加const的时候一定要加。

函数形参要加const的时候也一定要加。

13、等于4字节的数据,传引用和传值的效率是一样的。

因此像int、char这些简单的内置数据类型,不传引用也没太大关系。

14、不含有指针成员的类很有可能是不需要写析构函数的。

15、一个函数最终能否成为inline函数,是由编译器决定的。因此把所有的函数写成inline都没有关系。

16、头文件应该有防卫式声明。

#ifndef _MYSTRING_
#define _MYSTRING_
// ...
#endif

17、含有指针成员的类要有拷贝构造和拷贝赋值,而不含有指针成员的类可以直接使用编译器提供的默认的一套。

  • 因为,不含有指针成员的类,编译器一个一个bit地拷贝没有什么问题,这正是我们想要的。
  • 但是,含有指针成员的类,编译器拷贝指针的地址,这样的话,两个类里面的这个指针成员都指向了同一块内存,这不是我们所想要的。
  • 用系统默认的那一套就是浅拷贝。拷贝构造那种方式是深拷贝。
  • 含有指针成员的类,里面的指针成员指向的内存并不属于类自己。

18、字符串的时候,我们一定要考虑到字符串后面会有一个结束符。

String s1();
String s2("hello");
inline 
String::String(const char* cstr = 0) // 传进来的字段串是有结束符的(长度是6),但是 strlen("hello") 的长度是5.
{
    if(cstr){
        m_data = new char[strlen(cstr)+1];
        strcpy(m_data, cstr);
    }
    else{
        m_data = new char[1];
        *m_data = '\0';
    }
}

19、下面两句话:

String s2(s1);      // 拷贝构造:s2创建一份和s1一样大的内存空间.
String s2 = s1;     // 拷贝赋值:s2先杀掉自己(清空自己),然后再创建一份和s1一样大的内存空间.

20、拷贝赋值函数一定要考虑自我赋值的情况。

因为拷贝赋值函数,是先杀掉s2自己,如果这样子的话,在创建一份和s1一样大的内存空间的时候,将产生未定义行为。

21、Stack,栈,是存在于某作用域内的一块内存空间。

当调用函数的时候,函数本身就会形成一个stack用来存放它所接受的参数、返回地址、局部变量。

22、Heap,堆,或者叫 system

heap,是指由操作系统提供的一块global内存空间,程序可动态分配从中获取若干区块(blocks)。

23、static对象的生命在作用域之后仍然存在(因此它的析构函数不会在离开作用域的时候被调用),直到整个程序结束。

24、全局对象写在任何作用域之外,在全局作用域之中,其生命在整个程序结束之后才结束。

25、new 是先分配内存,再调用构造函数。

new一个对象,实际上编译器分解为3个动作:
Complex* pc;
void* mem = operator new(sizeof(Complex));  // 其内部调用 malloc(n)
pc = static_cast<Complex*>(mem);
pc->Complex::Complex(1,2);

26、delete 是先调用析构函数,再释放内存。

delete一个对象,实际上编译器分解为2个动作:
String::~String(ps);
operator delete(ps);    // 其内部调用 free(ps)

delete ps是调用类的析构函数,类的析构函数再去delete它自己指针成员指向的内存空间。

27、array new 一定要搭配 array delete 使用。

不然会造成内存泄漏,泄漏的不是类自己的内存空间,泄漏的是类的指针成员指向的内存。其实如果类没有指针成员,不用array delete也可以。

28、在32位的平台上,一个指针是4个字节的。

29、用new的方式去创建对象的时候:

  • debug调试模式层面上看如果一个类有两个double类型,类的大小不止两个double类型的大小。
  • release模式层面上看如果一个类有两个double类型,类的大小仅比两个double类型的大小大两个cookie(每个cookie4个字节)。
  • 上下cookie作用是记录内存的长度,方便回收内存。
  • 如果是array new,在VC编译器会加一个字节大小的内存空间存放new的数组的大小。

30、类的函数和static成员在内存中只保留一份,无论你创建了多少个对象。

31、静态函数与普通函数的区别在于静态函数没有this pointer。
所以,静态函数如果要处理数据的话,只能处理静态数据。

32、如果是静态数据,要在类的外部加定义(分配内存的才叫定义)。

例如,double Account::m_rate = 8.0;

33、父类的析构函数必须是virtual的,否则会出现未定义行为。

如果一个类,你觉得它现在或将来会成为父类,那么就将它的析构函数设置为virtual。

34、关于继承的三种函数:

  • ①没有virtual的函数,也就是普通函数:你不希望子类重新定义它。
  • ②virtual函数:你希望子类重新定义(override,覆盖),且你对它已经有默认定义。
  • ③pure virtual纯虚函数:你希望子类一定要重新定义它,你对它没有默认定义。
  • 其实纯虚函数是可以有定义的。

35、如果有一个类B,它的父类是A,它有一个成员变量是类C,则创建B对象的时,构造的顺序是先A,然后C,最后才是B自己。

对象b析构的顺序刚好相反,是先B,然后C,最后才是父类A。
(先构造A还是C,先析构A还是C,在不同的编译器可能有不同的表现)

36、在容器里面放的东西一定是一样大小,因此容器不存在对象,而是存放类类型的指针。

37、当设计一个基类的时候,如果你不知道子类的名字是什么、但是你又想知道子类(比如说想调用它的某个函数)。

可以考虑用原型的设计模式,即

  • ①父类A有个静态方法addPrototype(A *a);
  • ②父类的addPrototype方法可能是存放a到一个静态容器vec当中;
  • ③子类有一个静态变量(这个变量的类型是子类自己);
  • ④子类的私有的构造函数会调用addPrototype(this);

这样的话,当子类的静态变量创建的时候,父类就会得到子类指针。

  • ①父类有一个纯虚函数clone()=0;
  • ②子类必须实现该函数,该函数直接new自己去返回。

这样的话,因为前面已经得到了子类的指针,这样如果父类拿这个指针去调用clone函数,就可以得到该类的对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值