C++面向对象

一、类和对象、this指针

OOP语言的四大特征是什么?

  • 抽象
  • 封装、隐藏
  • 继承
  • 多态

类体内实现的方法会自动处理为inline函数。

类对象的内存大小之和成员变量有关

类在内存上需要对齐,是为了减轻cup在内存上的io次数

查看类对象的大小的指令:cl className.cpp /d1reportSingleClassLayout类名

一个类可以定义无数个对象,每个对象都有自己的成员变量,但是他们共享一套成员方法。

有一个问题:Q1:类中的成员方法是怎么知道要处理哪个对象的信息的?

A1:在调用成员方法的时候会在参数列表里隐式的给定对象内存的地址。如下所示:

类的成员方法一经编译,所有方法参数都会加一个this指针,接收调用该方法的对象的地址,即下图中的CGoods *this

二、掌握构造函数和析构函数

定义一个SeqStack类:

class SeqStack {  public:  SeqStack(int size = 10) :_top(-1), _size(size) {   _pstack = new int[size];  }  ~SeqStack() {   cout << this << "~SeqStack()" << endl;   delete[] _pstack;   _pstack = nullptr;  }   void push(int val) {   if (full()) {    resize();   }   _pstack[++_top] = val;  }   void pop() {   if (empty()) {    return;   }   --_top;  }   int top() {   return _pstack[_top];  }  bool empty() { return _top == -1; }  bool full() { return _top == _size-1; }  private:  int* _pstack;   int _top;   int _size;   void resize() {   int* ptmp = new int[_size * 2];   for (int i = 0; i < _size; i++) {    ptmp[i] = _pstack[i];   }   delete[] _pstack;   _pstack = ptmp;   _size *= 2;  } }; /**  运行过程 */ int main() {  SeqStack sq1;   for (int i = 0; i < 15; i++) {   sq1.push(rand() % 100);  }   while (!sq1.empty()) {   cout << sq1.top() << " ";   sq1.pop();  }   return 0; } 

三、掌握对象的深拷贝和浅拷贝

.data段的对象是程序启动的时候构造的,程序结束的时候析构的

heap堆上对象是new的时候构造的,delete的时候析构的

stack栈上的对象是在调用函数的时候构造的,执行完函数时析构的

如果对象占用外部资源,浅拷贝就会出现问题:会导致一个对象指向的内存释放,从而造成另一个对象中的指针成为野指针。所以就要对这样的对象进行深拷贝,在新的对象中重新开辟一块空间,使两者互不干涉。

注意:在面向对象中,要避免使用memcpy进行拷贝,因为对象的内存占用不确定,会因为对象中保存指针而造成浅拷贝。需要拷贝的时候只能用for循环逐一拷贝。

深拷贝:

 SeqStack& operator=(const SeqStack& src) {   cout << "operator=" << endl;   //防止自赋值   if (this == &src) {    return *this;   }   delete[] _pstack;//需要释放掉自身占用的外部资源   _pstack = new int[src._size];   for (int i = 0; i <= src._top; i++) {    _pstack[i] = src._pstack[i];   }   _top = src._top;   _size = src._size;   return *this;  }   SeqStack(const SeqStack& src) {   cout << this << "SeqStack(const SeqStack& src)" << endl;   _pstack = new int[src._size];   for (int i = 0; i <= src._top; i++) {    _pstack[i] = src._pstack[i];   }   _top = src._top;   _size = src._size;  } 

四、类和对象应用实践

类Queue:

#pragma once class CirQueue { public:   CirQueue(int size = 10) {   _pQue = new int[size];   _front = _rear = 0;   _size = size;  }   CirQueue(const CirQueue& src) {   _size = src._size;   _front = src._front;   _rear = src._rear;   _pQue = new int[_size];   for (int i = _front; i != _rear; i = (i + 1) % _size) {    _pQue[i] = src._pQue[i];   }  }   ~CirQueue() {   delete[] _pQue;   _pQue = nullptr;  }     CirQueue& operator=(const CirQueue& src) {   if (this == &src) {    return *this;   }   delete[] _pQue;//需要释放掉自身占用的外部资源   _size = src._size;   _front = src._front;   _rear = src._rear;   _pQue = new int[_size];   for (int i = _front; i != _rear; i = (i + 1) % _size) {    _pQue[i++] = src._pQue[i];   }   return *this;  }   void push(int val) {   if (full()) {    resize();   }   _pQue[_rear] = val;   _rear = (_rear + 1) % _size;  }  void pop() {   if (empty()) {    return;   }   _front = (_front + 1) % _size;  }   int front() {   return _pQue[_front];  }   bool full() {   return (_rear + 1) % _size == _front;  }    bool empty () {   return _front == _rear;  }    private:  int* _pQue;   int _front;   int _rear;   int _size;   void resize() {   int* ptmp = new int[_size * 2];   int index = 0;   for (int i = _front; i != _rear; i=(i+1)%_size) {    ptmp[index++] = _pQue[i];   }   delete[] _pQue;   _pQue = ptmp;   _front = 0;   _rear = index;   _size *= 2;  } };   

类String:

#pragma once #include <algorithm> class String { public:   String(const char* str = nullptr) {   if (str != nullptr) {    _pChar = new char[strlen(str) + 1];    strcpy(_pChar, str);   }   else {    _pChar = new char[1];    *_pChar = '/0';   }  }   String(const String& str) {   _pChar = new char[strlen(str._pChar)+1];   strcpy(_pChar, str._pChar);  }   ~String() {   delete[] _pChar;   _pChar = nullptr;  }   String& operator=(const String& str) {   if (this == &str) {    return *this;   }   delete[] _pChar;//需要释放掉自身占用的外部资源    _pChar = new char[strlen(str._pChar) + 1];   strcpy(_pChar, str._pChar);   return *this;  }  private:  char* _pChar;   }; 

五、掌握构造函数的初始化列表

初始化列表和写在构造体里有什么区别:

初始化列表会直接定义并且赋值;放在构造体里会先执行定义操作,在对定义好的对象赋值。

对象变量是按照定义的顺序赋值的,与构造函数中初始化列表的顺序无关。上图中的ma是0xCCCCCCCC,mb是10,ma未赋值。

六、掌握类的各种成员方法及其区别

普通成员方法和常成员方法,是可以重载的,常成员方法可以在对象声明为const的时候调用。

对象声明为const的时候,调用成员方法是通过const对象的指针调用的,而普通的成员方法默认生成的是普通的指针对象,不能直接赋值。

只要是只读操作的成员方法,一律实现成const常成员方法

三种成员方法:

七、指向类成员的指针

class Test { public:  void func() { cout << "call Test::func" << endl; }  static void static_func() { cout << "call Test::static_func" << endl; }   int ma;  static int mb; };  int Test::mb=0;  int main() {   Test t1;  Test *t2 = new Test();//在堆上生成对象,并用指针指向   //使用指针调用类成员方法(前面要加类的作用域Test::)  void (Test:: * pfunc)() = &Test::func;  (t1.*pfunc)();  (t2->*pfunc)();   //定义指向static的类成员方法  void(*pfunc1)() = &Test::static_func;  (*pfunc1)();   //使用指针指向类成员变量,前面要加类的作用域Test::  int Test::* p = &Test::ma;  t1.*p = 20;  cout << t1.*p << endl;   t2->*p = 30;  cout << t2->*p << endl;   int* p1 = &Test::mb;  *p1 = 40;  cout << *p1 << endl;   delete t2;  return 0; } 

输出为:

商匡云商
Logo
对比商品
  • 合计 (0)
对比
0
购物车