草庐IT

三、C++面向对象-类和对象那些你不知道的细节原理

woden3702 2023-03-28 原文

一、类和对象、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;
}

输出为:

有关三、C++面向对象-类和对象那些你不知道的细节原理的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  4. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  5. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  6. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  7. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  8. Ruby——嵌套类和子类是一回事吗? - 2

    下面例子中的Nested和Child有什么区别?是否只是同一事物的不同语法?classParentclassNested...endendclassChild 最佳答案 不,它们是不同的。嵌套:Computer之外的“Processor”类只能作为Computer::Processor访问。嵌套为内部类(namespace)提供上下文。对于ruby​​解释器Computer和Computer::Processor只是两个独立的类。classComputerclassProcessor#Tocreateanobjectforthisc

  9. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  10. ruby - 更改 ActiveRecord 中对象的类 - 2

    假设我有一个FireNinja我的数据库中的对象,使用单表继承存储。后来才知道他真的是WaterNinja.将他更改为不同的子类的最干净的方法是什么?更好的是,我很想创建一个新的WaterNinja对象并替换旧的FireNinja在数据库中,保留ID。编辑我知道如何创建新的WaterNinja来self现有FireNinja的对象,我也知道我可以删除旧的并保存新的。我想做的是改变现有项目的类别。我是通过创建一个新对象并执行一些ActiveRecord魔法来替换行,还是通过对对象本身做一些疯狂的事情,或者甚至通过删除它并使用相同的ID重新插入来做到这一点,这是问题的一部分。

随机推荐