c++ 常见的一些问题总结

目录

目录

1.深拷贝(string)

深拷贝的实现()

2.迭代器失效(vector--insert,earse)

3. 头文件展开问题

4.vector 的深浅拷贝

解决浅拷贝的问题

深拷贝的现代写法

 vector ("="的赋值重载)

现代写法



1.深拷贝(string)

深浅拷贝:

浅拷贝:在拷贝构造过程中由于对自定义类型中如果存在需要在堆上开空间(new)的情况,那么在拷贝构造的时候新的对象也会指向同一个堆上的空间地址。那么在调用析构函数的时候此时就会造成对堆上new出来的对象释放两次,且指向这个空间任意一个对象进行对此空间修改的时候都会造成另一个对象的内容也随之发生改变。这个就是浅拷贝带来的问题。--解决的方法就是深拷贝

深拷贝:为了解决浅拷贝的问题,这里我们要对针对浅拷贝new的一个空间进行处理-我们只需要当拷贝构造的时候对新的对象也开辟一个同等大小的内存空间在将数据拷贝过去这样就解决了两个对象指向同一个内存空间。但这里我们应该注意在拷贝的时候我们应该要使用&传参这样可以大大减少拷贝构造。--

深拷贝的实现()

//深拷贝的实现方式1 

string(const string& s)
	:_str(new char [strlen(s._str)+1])// 创造一个同等大小的空间
	{
	strcpy(_str, s._str);

}
// 对于"="的赋值重载 为了避免空间两个大小不一致我们就先开辟一个需要存放拷贝内容大小的空间
//	然后释放之前的空间 再将需要拷贝的内容存放到新开辟空间内。
string& operator=(const string &s) 	
{
if(this!=s)
{
string * tmp=new string[strlen(s._str)+1]; //这里+1是为了存放\0
strcpy(tmp,s._str);
delete []_str;
_str=tmp;
}
return *this;
}
//深拷贝的另一种做法
//改进深拷贝--想法我们借用一个中间变量去完成这些事情之后将内容给到this即可
//		string(string& s) 
//			:_str(nullptr)
//		{
//			string tmp(s._str);// 这里是去调用构造函数来创造空间并将s._str的内容拷贝给tmp
//
//			swap(_str, tmp._str);// 将_str和tmp所指向的空间进行交换
//			// 因为tmp是一个临时变量出了作用域就要调用析构函数,但是此时tmp所指向的内容已经为
//			//_str所指向内容,如果没有对_str初始化那么交换后tmp指向的也是一个随机值
//			//我们不能对指向随机值的空间进行析构 所以我们应先将_str赋予初始值nullptr;
//			//tmp._str = nullptr;或者这样也可也
//		}

string (string&s)
_str(nullptr)
{
string tmp(_s.str);
swap(_str,tmp._str);
}
// 或者
string (string&s)
{
string tmp(s._str);
swap(_str,tmp._str);
tmp._str=nullptr;
}
对于‘=’的重载

string& operator=(string s) //这里没有用&
//这里是用了拷贝函数去完成了开辟空间 在创造s的时候就回去调用
	                       //拷贝构造这样就可以s拥有了一个新的空间且有了str的内容
{
	swap(_str, s._str);

	return *this;
}

2.迭代器失效(vector--insert,earse)

迭代器失效分为两种:1.由于新开辟了空间将原来的内容拷贝到新的空间上造成指向原来地址的指针成为野指针。2.没有开辟新的空间但是由于数组内容的移动,那么迭代器原来指向的内容发生了改变,那么也属于迭代器失效,对于vs下无论是那种情况对原来的迭代器解引用的时候都会报错。其中一般在vector中的earse 以及insert中出现。 为了避免出现迭代器失效的问题C++利用返回值来解决此问题,insert()返回的是插入的数值前的位置的迭代器,earse()返回的是消除的位置的后一个迭代器。

vector<int> v;
  
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        auto begin = v.begin();
        while (begin != v.end()) 
        {
            if (*begin % 2 == 0)
            {
                begin = v.erase(begin);// 所以为了避免迭代器失效带来的问题 所以会指向被函数调用擦除的最后一个元素之后的元素的新位置的迭代器。
            }
            else 
            {
                begin++;
            }
              
        }
// vector 中的earse和insert实现
iterator insert(iterator pos,const T& val) 
		{
			assert(pos >= _start && pos <= _finish);
			int len = pos - _start;
			if (_finish == _endofstorage) 
			{
				size_t newcapactity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapactity);
			}
			pos = _start + len;
			auto end = _finish+1;
			while(end>pos)
			{
				*(end) = *(end - 1);
				end--;
			}
			*(pos) = val;
			_finish++;
			
			return pos;
		}
		iterator earse(iterator pos)
		{
			assert(pos >= _start && pos <_finish);
			auto tmp = pos;
			while (tmp != _finish) 
			{
				*(tmp) = *(tmp + 1);
				tmp++;
			}
			_finish--;
			return pos;
		}

3. 头文件展开问题:

//假设 vector.h中使用了cout。
#include<iostream>
#include"vector.h"
//那么此时编译就会出现问题,原因是头文件在编译阶段会展开,且无论是成员变量还是成员函数都是遵守的向上
//查找 那么此时vector被展开,且vector中使用了std的成员函数那么此时向上查找就无法找到std的,那么此时就会报错。
using namespace std;
// 正确的方式
#include<iostream>
using namespace std;
#include"vector.h"

4.vector 的深浅拷贝

对于vector而言也存在深浅拷贝问题(浅拷贝的问题:1.当拷贝多个对象的时候任意一个对象的修改都会造成其他对象也发生改变。2.当如果存在自定义类型的时候且含有在堆上new的空间的时候在调用析构函数的时候就会造成对一个空间多次析构)。

解决浅拷贝的问题:

深拷贝的传统想法 1.开辟同等大小的空间 然后将原对象的内容复制即可。

// V1(V);
vector(const vector<T>& v) 
		{
			_start = new T[v.capacity()];
			memcpy(_start, v._start, sizeof(T) * v.size());
			_finish = _start + v.size();
			_endofstorage = _start + v.capacity();
		}
void reserve(size_t n)
		{
			if (n > capacity())
			{
				int sz = size();
				T* tmp = new T[n];
				memcpy(tmp, _start, sizeof(T) * sz);
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}

		}
//第二种 思路:将自己的数据先置为空 然后将v的数据依次插入
vector(const vector<T>&v)
           :_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
{
reserve(v.capacity());
for(const T&e:v)
{
push_back(v);
}


}

深拷贝的现代写法

现代的写法创造一个临时变量通过去调用构造函数然后与其进行交换,这里于string不同,由于string可以同过自己成员变量一次拿到字符且由于string自己含有'\0'就可以完整的通过构造函数拿到所有的字符,但是vector无法拿到,所以在就要通过迭代器来一次获取数据。

//由于不能获取到原数据,这里采用了迭代器来获取,这里使用模板的好处就是可以在构造过程的时候可以
//使用任何类型(不仅是内置类型,自定义类型也支持)只要和T的类型对应即可
template <class InputIterator>
	vector (InputIterator first, InputIterator end)
		:_start(nullptr)
		, _finish(nullptr)
		, _endofstorage(nullptr)

	{
		while (first!= end) 
		{
			push_back(*first);
			first++;
		}
	}
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr) 
		{
			
			vector<T> tmp(v.begin(), v.end());
			
			swap(tmp);
		}
		void swap(vector<T>& v) 
		{
			std::swap (_start, v._start);
			std::swap (_finish, v._finish);
		
			std::swap (_endofstorage, v._endofstorage);

		
		}
void test()
{
     string s("hello");
vector<char>t(s);// 这里由于使用的是模板的迭代器所以可以支持不同类型的拷贝
		

}

 vector ("="的赋值重载)

对于“=”的赋值重载也是分为两种传统写法和现代写法。

传统写法的想法:将原对象的数据先清空,然后在新对象的内容一次赋值给原对象。

vector <T>& operator =(const vector<T>& v)
		{
			 if (this != v) 
			 {
				 delete[]_start;
				 _start = _finish = _endofstorage = nullptr;
				 reserve(v.capacity());
				 for (auto& e : v) 
				 {
					 push_back(e);
				 }
			 }


			 return *this;
		}

现代写法

现代写法通过一个临时变量来过度,将内容先拷贝到临时变量内,然后通过交换可完成。

	vector <T>& operator =(const vector<T> v) //这里是巧妙的利用了深拷贝的拷贝构造将v对象的内容//全部拷贝给了临时对象v;
		{
			swap(v);
			return *this;
		}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 多重背包问题是指在给定容量和物品的价值和重量的情况下,如何最大限度地装入物品,使得总价值最大化的问题。它的模板是:给定N种物品和一个容量为V的背包,每种物品有无限件可用,每件物品的重量是w[i],其价值是v[i]。求解将哪些物品装入背包可使价值总和最大。 ### 回答2: 多重背包问题是一个经典的组合优化问题,它是在0/1背包问题的基础上进行了扩展。在多重背包问题中,每个物品可以被选择的次数不再是1次,而是有一个确定的上限k次(k>1)。我们需要选择一些物品放入背包中,使得它们的总体积不超过背包的容量,并且使得它们的总价值最大化。 要解决多重背包问题,可以使用动态规划的方法。首先,我们定义一个二维数组dp[i][j],其中i表示前i个物品,j表示背包的容量。dp[i][j]表示当只考虑前i个物品、背包容量为j时,能够获取的最大价值。然后,我们可以使用如下的状态转移方程来计算dp[i][j]的值: dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]]+w[i], dp[i-1][j-2v[i]]+2w[i], ..., dp[i-1][j-kv[i]]+kw[i]) 其中,v[i]表示第i个物品的体积,w[i]表示第i个物品的价值,k表示第i个物品的可选次数。上述状态转移方程的意义是,我们可以选择不取第i个物品,或者分别取1次、2次、...、k次第i个物品,选择这些情况下的最大价值。 最后,我们可以通过遍历所有的物品和背包容量,计算出dp[n][m],其中n表示物品的个数,m表示背包的容量。dp[n][m]即为问题的解,表示只考虑前n个物品、背包容量为m时能够获取的最大价值。 综上所述,多重背包问题的解决方法是利用动态规划,通过定义状态转移方程和计算数组dp的值,找到问题的最优解。希望以上介绍对您有所帮助。 ### 回答3: 多重背包问题常见的背包问题之一,与0-1背包问题和完全背包问题类似,但有一些区别。 在多重背包问题中,给定n个物品和一个容量为V的背包,每个物品有两个属性:重量w和价值v。同时,每个物品还有对应的个数限制c,表示该物品的数量最多可以选择c次。 我们需要选择物品放入背包,使得背包的总容量不超过V,同时物品的总价值最大。 多重背包问题可以用动态规划来解决。 我们可以定义一个二维数组dp,其中dp[i][j]表示前i个物品中选择若干个物品放入容量为j的背包时的最大价值。 根据多重背包问题的特点,我们需要对每个物品的个数进行遍历,并依次判断放入背包的个数是否超过c。 具体的状态转移方程为: dp[i][j] = max(dp[i-1][j-k*w[i]] + k*v[i]),其中0 <= k <= min(c[i], j/w[i]) 最后,需要注意的是多重背包问题的时间复杂度较高,为O(N*V*∑(c[i])),其中N是物品的数量,V是背包的容量,∑(c[i])表示物品的个数限制的总和。 总结而言,多重背包问题是在0-1背包问题和完全背包问题基础上的一种更复杂的情况,需要对每个物品的个数进行遍历和判断,采用动态规划求解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值