C++学习笔记

1. C和C++枚举的区别:
enum Sea
{
Spr, Sum, Autu, Win
};
int main()
{
Sea s = 100; //C语言中这么写可以,可以将其他值赋给Sea类型变量
//但是C++中不可以,必须从Sea里面的四个数中选


return 0;
}

2. extern "C" {
是为了不让这里面的函数产生倾轧。
   }


3. &符号前有数据类型时,是引用。其他皆为取地址。
   或者&放在=左边的是引用,=右边的&是取地址

4. const char* p = "linux";
   const char* &rp = p; //定义指针的引用

5. 引用的目的就是为了干掉指针。故有指针的指针,但是没有引用的引用。


6. int* & 和 int& *
   第一个是合法的,叫指针的引用;第二个是不合法的,叫引用的指针(与设计理念相背)
   引用就是由指针包装的,就是为了干掉指针,第二个则是再次拆包,与设计理念不符。
   
7. int x, y, z;
   int *p[] = {&x, &y, &z}; //OK 指针数组
   int &rp[] = {x, y, z};   //error 引用数组(C++中没有引用数组,会造成数组地址不连续)


8. int arr[5] = {1,2,3,4,5}; //arr是int[5]类型的
   int(&arr1)[5] = arr;       //OK 可以对数组名进行引用


9. C++中访问全局变量用作用域运算符::
int v = 10;
int main()
{
int v = 5;
cout << v << endl;    // 5 默认是局部的
cout << ::v << endl;  // 10 用::访问全局变量
return 0;
}


10. string是命名空间std下的一个类


11. 通过返回成员变量的引用可以修改成员变量的值
int& Date::Year() // Year()是成员函数
{
return year;  // year可以是private成员变量
}
main:
Date d(2017, 11, 14);
d.Year() = 2015;    // 可以在类外修改year的值


12. C++类中若未自己提供构造函数,系统默认会提供一个无参构造;若自己提供了一个构造函数,系统则不再默认生成无参构造。
有参构造中可以给参数一个默认值,在main中定义对象时可以赋初值或者不赋也可以。


13. 析构函数:对象消失的时候,自动调用,用于对象销毁时的内存处理工作。


14. class A{int a; int b; int c; void func{}};
A a; sizeof(a) = 12; 对象大小只和数据成员有关,和函数代码无关。 


15. 构造函数主要用来初始化类的数据成员,以及申请空间
析构器主要用来析构数据成员中含有指针的成员所指向的那段空间。
释放对象指针ps所指向的空间之前,会先去析构器中释放name指针所指向的空间,再释放自己。


16. 数据成员的初始化顺序,跟数据成员的声明顺序有关,跟初始化表:后的顺序无关。
初始化列表还一个好处是可以允许参数和数据成员同名。例:A(int age, int weight):age(age), weight(weight){}


17. 一个类默认有无参构造、拷贝构造、析构函数、赋值运算符、取址运算符
class Empty
{
 public:
 Empty();               // 缺省构造函数
 Empty( const Empty& ); // 拷贝构造函数
 ~Empty();              // 析构函数
 Empty& operator=( const Empty& ); // 赋值运算符
 Empty* operator&();               // 取址运算符
 const Empty* operator&() const;   // 取址运算符 const
};


18. 系统默认会提供拷贝构造(默认提供的是浅拷贝),但是如果自己实现了,系统就不再提供默认的了。
默认的拷贝构造函数会将ms1对象的所有数据成员赋值给ms2对象的所有数据成员。
即使是指针成员也是一样赋值。这样就会造成析构时,double free。
主要问题: double free


19. 含有堆空间的时候要自实现拷贝构造函数。(给当前对象的指针成员申请堆空间,再将要拷贝的对象的指向成员指向的堆空间的数据拷贝进去。)


20. 拷贝构造描述的是一种,用一个已有对象完成另一个对象从无到有的创建过程
有两种表达方式:        
1.string s1 = "china"; string s2(s1);
2.string str1 = "linux"; string str2 = str1; 


21. 系统默认也会提供赋值运算符重载函数(默认提供的是浅拷贝),但是如果自己实现了,系统就不再提供默认的了。
主要面临问题:1.自己执向空间内存泄漏 2.double free 3.自赋值(a = a)

22. 传引用等价于扩展了原对象的作用域。


23. 栈上的对象是可以返回的,但是不能返回栈上的引用(除非返回对象本身,即this;或者返回()中的引用对象)。


24. 关于析构中delete的问题:应该是如果类中数据成员有指针时,且该指针申请了一段堆空间,释放该空间时,
就可以在析构函数中释放。如果是在某个成员函数中又定义一个栈指针且申请了一段堆空间,则在该函数末前
就要释放掉,不能跑到析构函数中去释放它。


25. 创建对象数组的时候,要给每个对象都初始化,或者给构造函数参数提供默认值,或者提供无参构造函数(二段式构造,再提供一个init函数,完成实事,在无参构造中调用init)


26. new一个堆对象,会自动调用构造函数,delete一个堆对象会自动调用析构函数


27. const修饰类的数据成员的时候,初始化位置只能在参数列表里面。
const修饰成员函数,位置:函数声明之后,实现体之前。
const修饰成员函数时,该函数内不能修改类的数据成员,函数中自己定义的变量是可以修改的
const修饰的成员函数可以访问类的const成员和非const成员,但是不能修改。
const修饰的成员函数只能访问const成员函数。
const函数和非const函数可以构成重载
const修饰函数,是从函数层面,不修改数据。
const修对象, 是从对象层面,不修改数据,因而只能调用const成员函数。
const对象只能调用const成员函数;
非const对象优先调用非const成员函数,若无,则调用const成员函数。
const函数在声明处要加const,在定义处也要加const。

28. 类中有static数据成员时,该成员不占用类的存储空间,且该static数据成员供族类对象共享。
类的普通数据成员是在创建类对象的时候才开始开辟空间;而类的static数据成员在类声明时
就已经开辟了空间,存储空间在data段的rw段。
类的static数据成员都要初始化:类内定义(加static),类外初始化(不加static):int A::a = 0;
类的static数据成员可以通过对象访问,也可以通过类名+::来访问。

static成员函数作用只有一个:用于管理static数据成员(当static数据成员私有化的时候,就要用到static成员函数)。
static成员函数既可以被对象调用,也可以被类名加::调用。
static成员函数因为属于类,所以没有this指针,不能访问非static数据成员及成员函数。


29. static const a = 29; static const 数据成员是就地初始化。


30. 函数(全局,或者类的成员函数)可以做友元,称为友元函数。
将友元函数声明在要访问的类中:friend void dis(A& t);
见到友元就要想到通过类对象访问数据成员


31. 把B声明为A的友元,就可以在B中通过A的对象访问A的私有成员。

32. 函数重载必须发生在同一作用域。

33. 流输入输出运算符重载只能在类外定义,并在类内声明为友元,因为第一个对象是cout(或者叫流对象),
而类中的操作符重载函数第一个对象默认是this。之所以传的是引用,输出也是引用,是因为cout一直存在。
ostream& operator << (ostream &os, const Complex &c);

34. 当在一个类中用到某个类的对象的引用的时候,需要在类前声明该类。
例如:
class A; //需要加这句类声明
class B
{
B(){}
void display(A& a){} //此处用到了A对象的引用,需要在上面加A类的声明
};


35. 类型转换构造函数
Point3D(const Point2D &p2)
    {
        cout << "Point3D(const Point2D &p2)" << endl;
        this->_x = p2._x;
        this->_y = p2._y;
        this->_z = 8;
    }


36. implicit(隐式转化) explicit(显示转化)
static_cast<类型>(变量)显示类型转化。可以将某个类对象强转成另一个类对象:A a; static_cast(B)(a);


37. 继承方式(即:继承自父类)不会影响子类成员的访问方式,影响了父类中的成员在子类中的访问权限


38. 基类的数据成员和成员函数会被子类全盘接收(接收来不代表一定能访问),但除了构造器和析构器(即:在子类中不能直接调用父类的构造器(只能在子类的参数列表这个调用)和析构器)。


39. 基类的私有数据成员无论以何种继承方式,到了子类都是不可见的,即子类都不能访问。


40. 继承某个类,就要在子类头文件中包含要继承的类的头文件。在子类实现函数的.c文件中直接包含子类的头文件即可(因为子类头文件中已经包含了父类头文件了)


41. 父类的私有数据成员在子类中是存在的但是是不可直接访问的,需要在子类中通过调用父类的成员函数来访问父类的私有数据。


42. 派生类构造函数的语法:
派生类名::派生类名(参数总表)
:基类名(参数表),内嵌子对象名(参数表) //内嵌子对象的这个参数表就是该子对象所在类的构造器中的参数列表;如果是内嵌对象指针,则在括号中写new 对象类名(子对象构造参数列表)
{
派生类新增成员的初始化语句; //也可出现地参数列表中
}


43. 父类如果有无参构造器或者有参构造但参数有默认值则在子类构造器的参数列表中可以不用显示调用父类构造器。
总结:只要父类中有标配(无参构造器或者有默认参数构造器),子类就不需要在参数列表中调用父类构造器。
父类如果没有标配,则让子类解决(即在子类参数列表中调用父类构造器,无需孙类解决),孙类只管他上级的构造问题。
爷爷类和父类中有的数据成员,在子类中全盘接收,因而子类在构造器参数列表中要提供爷爷类的数据成员参数,爸爸类的数据成员参数,
和自己的数据成员参数。但是只需在参数列表中调用爸爸类的构造器就可以初始化爷爷类和爸爸类的所有数据成员,因为在爸爸类的
构造器的参数列表中又调用了初始化爷爷类的析构器。
子类只需对爸爸类负责,不需对爷爷类负责,爷爷类由爸爸类负责。


44. class Father
class Son : public Father
class GrandSon : public Son
A a; //A也是一个类,且a对象是GrandSon类的数据成员。
GrandSon gs; //创建对象gs的时候,先调用Father构造器,再调Son构造器,再调用类对象A的构造器,最后调自己GrandSon构造器。
//对象析构的时候,先析构GrandSon析构器,再析构A的析构器,再析构Son析构器,最后析构Father析构器。
之所以在子类构造函数的参数列表中调用父类构造器是因为:子类对象的部分数据成员来自父类,需要通过父类构造器来完成这部分数据成员的初始化。
 
45. 当一个类被分成.h文件和.c文件的时候,函数参数的默认值要写在.h文件中,即函数声明处。


46. 在一个类Graduate的private中可以定义另一个类Birthday的对象birth作为Graduate类的私有数据成员。通过Graduate类的头文件中包含"birthday.h"就可以
在Graduate类的成员函数中直接通过birth对象来访问Birthday类的成员函数。

47. shadow(或叫名字隐藏,即非虚函数的情况)
子类中如果定义了和父类相同名字的数据或函数。则会把父类数据或函数隐藏掉。
函数名相同即可构成shadow,与函数重载不一样。


48. 当有父类子类时,父类中有个函数,会继承到子类,全局还有个函数与父类中的函数同名,
子类对象调用该同名函数时,如果子类对象调用的函数类型与全局函数类型相同,则会调用这个全局函数。

49. 中间局部变量不可以赋给一个引用类型的变量,因为该局部变量很快就消失了,但是可以赋给一个const&的变量,作为该变量的初始化


50. public          public           public
pub      pub             pub              pub
pro      pro             pro              pro
pri      inaccess        inaccess         inaccess
三个访问权限区别:
//public 传承接口, 传承数据
//protected 只传承数据
//private   隐藏数据用的


51. 多继承中的虚继承
关于继承问题,关键是看对象,看这个对象是属于哪个类的。以及该对象中有哪些数据成员(可借用sizeof来计算)


52. 虚基类及间接类的初始化
class A{
A(int i){}
};
class B:virtual public A
{
B(int n):A(n){}
};
class C:virtual public A
{
C(int n):A(n){}
};
class D:public B,public C
{
D(int n):A(n),B(n),C(n){}
};


53. 动多态形成的条件:
1.父类中有虚函数;
2.子类override(重写)了父类中的虚函数;
3.将子类对象地址赋给了父类指针,或将子类对象赋给了父类对象的引用。并发生虚函数的调用。
注意:当父类虚函数与实现体分离的时候,类内的虚函数声明前要加virtual,实现体函数前不需要加
     这点与static成员很像。


54. overload(函数重载): 同一作用域,函数名相同,参数列表不同。
shadow(名字隐藏,覆盖):发生在父子类的同名成员(包括数据成员和函数(只要同名即可))。
override(函数重写): 发生在父子类中,父类中函数有virtual声明的函数,子类重写的函数必须(同参、同名、同返回)


55. MainWindow(孙类) inherits QMainWindow(子类) inherits QWidget(父类)
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event)该函数来自QWidget类的虚函数
在子类QMainWindow中未实现,我们可以在孙类MainWindow中重写该虚函数,修改实现体内容,完成自己想要的操作。
并通过父类或者祖父类指针调用它。

56. 1. 含有纯虚函数的类,称为抽象基类,不可实列化。即不能创建对象,存在的意义就是被
  继承,给族类提供公共接口(虚函数),java 中称为 interface。
2. 纯虚函数只有声明,没有实现体,被“初始化”为 0。
3. 如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,则该虚函数在派生类
  中仍然为纯虚函数,派生类仍然为纯虚基类。
class Shape
{
public:
Shape(int x, int y):_x(x), _y(y){}


virtual void draw() = 0;


protected:
int _x;
int _y;
};


57. 虚析构:含有虚函数的类,析构函数也应该声明为虚函数。在 delete 父类指针的时候,会调用子
类的析构函数,实现完整析构。
当一个类中有虚函数的时候,将其析构函数一并virtual
含有虚函数的父类,其析构函数一定要virtual(如果父类析构器不加virtual,则当delete父类指针的时候,
不会调用子类的析构函数)。子类析构也可virtual。


58. 声明为虚函数的一些限制条件:
1)只有类的成员函数才能声明为虚函数
虚函数仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数。
2)静态成员函数不能是虚函数
静态成员函数不受对象的捆绑,只有类的信息。
3)内联函数不能是虚函数(内联函数是内嵌的编译期间就确定的,虚函数是动态的,运行时确定的)
4)构造函数不能是虚函数
构造时,对象的创建尚未完成。构造完成后,才能算一个名符其实的对象。
5)析构函数可以是虚函数且通常声明为虚函数。


59. 函数模板中的 返回值类型必须显示指定。




















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值