C++面向对象笔记

NEW与Delete

以如下的代码为例

int *p = new int;
int *q = new int[10];

Student *pp = new Student();
Student *qq = new Student[10];
// 假设一个Student对象占用16字节

new首先分配一块地址空间,然后**调用构造函数**,并返回一个指针,该指针指向地址空间的首地址。
为了记录不同的空间,C++在后台维护一张表,记录了new的对象的首地址和长度。
在执行完上述代码以后,表中的表项为
| 首地址 | 长度 |
| — | — |
| &p | 4B |
| &q | 40B |
| &pp | 16B |
| &qq | 160B |

考虑以下代码,通过delete关键字来释放空间

delete p;

delete[] q;

delete pp;

delete[] qq;
// delete qq;

简单地记一个原则:**如果new的时候带[ ],那么delete的时候也要带[ ].**
delete做的事情,是调用对象的析构函数(不同表项对应的析构函数在编译时就会依靠编译器来写入),然后将所占用的空间释放掉,并从table中删除表项。
如果new的时候有带[ ],delete的时候不带[ ],会产生什么后果?
以代码段中的qq为例,qq的首个Student对象会被析构,但是后面9个Student对象不会被析构,然后这10个对象占用的地址空间会被清除,同时表项会被删掉。这就有可能产生未被回收的内存空间。
注意: C++不允许创建带参数构造函数类的对象数组,
对象数组中的类必须带有无参数的默认构造函数(且必须有声明和定义)!

关键字Class与Struct的区别

区别仅在与类内成员变量和成员函数的默认访问权限。
若无指定,Class的成员变量和函数默认为private
若无指定,Struct的成员变量和函数默认为public

private的问题

private的私有是对于类与类之间的私有,而同一个类的不同对象的成员变量是可以相互访问的。

Class a{
private:
   int p;
public:
   void read(a *n){
      if (n !=NULL){
      cout<< n->p << endl;
         }
	 }
	 a(int value){
	 this -> p = value;
	 }
	 
int main(){
   a aa(10);
	 a bb(20);
	 a* p = &aa;
	 bb.read(p);
	 return 0;
}

以上面的代码为例,定义了一个类a,包含有int型成员变量p,其中成员函数read接收一个类a的指针,并返回其成员变量p的值。接下来在main函数中,实例化了两个a对象aa和bb,并且试图通过bb访问aa的private成员变量p,结果是访问成功了。

Class b{
   public:
	   void read(a *n){
	      if (n !=NULL){
	      cout<< n->p << endl;// 编译不通过
	         }
		 }
	 
};

接下来试图定义一个b类做相同的事情,结果是编译不通过。

友元对象/函数

若要使b类可以访问a类的私有成员变量(以上面的例子),则可以在a类中声明b类为友元类。

    Class b;//先给出声明,否则编译会不通过
Class a{
friend b;//声明友元类
private:
   int p;
public:
   void read(a *n){
      if (n !=NULL){
      cout<< n->p << endl;
         }
	 }
	 a(int value){
	 this -> p = value;
	 }
//接下来再给出Class b的定义。

初始化列表

第一条,构造函数中的赋值,一定要使用初始化列表进行初始化
即便是基本数据类型(如int、float等)也要如此。
第二条,父类的构造函数初始化也必须使用初始化列表。
如果某个对象的成员变量是另一个类,若不使用初始化列表,则当该对象初始化时容易出错。

  class a{
private:
       int value;
  public:
     a(value):value(value)// 初始化列表
		 {// 函数体
		 };
		 
};

继承

一个类继承自另一个类时,如b继承自a,则a的所有public以及protected方法与变量,a都可以访问与使用,但private不可以访问。

class A{
public: A(int i);
        A();
				void print();
protected:
        void set();
private:
        int i;
};

class B:public A{//继承的语法,B继承自A, 则B是A的子类
public: void reset(int i){set();}//子类内可以调用父类的protected接口
};

int main(){
B b;
b.set();// 此句编译不通过,子类不能在其他函数直接调用protected
b.reset();// 可以通过
}

函数重载的隐藏规则

假设父类中有一个方法print(),在该类中还重载了print(int A), 另外有一个子类A继承自该父类,A中有自己的一个方法print(),那么该父类的两个方法不会被继承(即被隐藏了,无法被调用)
即 子类和父类中出现同名函数,父类的函数被隐藏。


class A{
public: A(int i);
        A();
				void print();// 父类不带参数print
				void print(int n);// 父类带参数print
protected:
        void set();
private:
        int i;
};

class B:public A{
public: 
      void reset(int i){set();}
			void print();// 子类不带参数print
       
};

int main(){
   B b;// 实例化一个子类的对象
	 b.print();// 调用子类的print
	 b.print(10);// 错误,父类的两个print被隐藏
	 b.A::print();// 正确,成功调用父类的无参数print
	 b.A::print(10);// 正确,成功调用父类的带参数print
	 
}

函数定义默认值

  1. 函数参数的默认值只能在声明时给出(写在.h文件中)
  2. 带有默认值的参数排在右边
     void a(int i,int j=0,int h=0);// 正确
    void b(int i=0, int j, int k);// 错误
  3. 给参数赋予默认值是编译器进行编译时干的事情,与运行时无关。
  4. 建议不使用默认值,会造成阅读困难,且不安全。

inline函数

用法

声明和定义只能放在.h文件。

为什么使用内联函数?

在执行主函数main时,当需要调用其他函数时,就必须进行压栈的操作(参数入栈->地址入栈->地址出栈->参数出栈)
但是有些简单的函数(例如类中用于访问private成员的public接口)这样进行执行就会浪费资源
因此对这些比较简单的函数,将他们设为内联函数inline 。
如此做之后,编译器在编译时,不会将其编译成压栈入栈的操作路线,而是直接将所执行的代码直接插入到调用该函数的地方

tips

当声明一个类时直接在声明体内定义某些方法,则这些方法会被认为是inline函数(Any function you define inside a class declaration is automatically an inline function.)

哪些函数值得使用inline

  1. 运算少的小函数,两到三行。
  2. 需要被频繁调用的函数,特别是在循环体内的。

哪些函数不值得使用inline

  1. 非常大的函数,超过20行
  2. 递归调用的函数(因为递归本身就需要用到栈)

const

const值的使用

const int class_size =12;
int finalGrad[class_size];// OK

int x;
cin>>x;
const int size = x;
double classAverage[size];// error

为什么当const需要cin时不能成为数组长度的变量?
因为声明一个数组时,编译器需要知道该数组明确的长度,才能够在堆栈区给出足够大的预留空间,该段代码中的x需要cin给出值,因此是不确定的数不能作为数组的长度。

const指针的含义

char * const q = "abc";// 指针q是const
*q = 'C';// OK,指针所指向的内存块的内容可以改变
q++; // 指针的值不能改变

const char *p = "ABCD";
// (*p) 是一个const的char类型变量
*p = "D";//error
p++;// legal

const的含义是指:不能通过该变量改变该内存块的值。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!