C++中需要注意的细节整理

1. C++ 在delete指针后赋值为nullptr

①C++标准规定:delete空指针是合法的。
② delete是释放指针指向的内存,并不是指针本身占有的内存。所以delete后,指针还是指向那个区域,并未清零,下次使用时,会出现空间不能访问的异常,so delete后要赋值为空。

2. c++中赋值表达式返回的是左值的引用

①赋值运算符的重载:浅拷贝(单纯使两个指针指向同一个地方)与深拷贝(重新开辟一块内存,将要复制的指针指向的地方的内容复制过来)

注:减少深拷贝次数的方法:右值引用(右值:一般来说,不能取地址的表达式,就是右值, 能取地址的,就是左值)
示例:
class A { };
A & r = A(); // error , A()是无名变量,是右值
A && r = A(); //ok, r 是右值引用
移动构造函数:参数右值引用
将s.str指针赋值给str,然后令s.str指向新开辟的空间,效率高,但是改变了s.str指针指向的内容(临时对象不影响)
与深拷贝区别:删除str指向的空间,重新开辟,然后将s.str内容复制过来

}// move constructor
String(String && s):str(s.str) {
 cout << "move constructor called"<<endl;
  s.str = new char[1]; 
  s.str[0] = 0;
}
调用方法:
template <class T> 
void MoveSwap(T& a, T& b) {
T tmp(move(a));// std::move(a)为右值,这里会调用move onstructor 
a = move(b);// move(b)为右值,因此这里会调用move assigment
b = move(tmp);// move(tmp)为右值,因此这里会调用move assigment 
}

②赋值运算符“=”只能重载为成员函数
如不定义自己的赋值运算符,那么S1=S2实际上导致 S1.str和 S2.str 指向同一地方。

注意点:
	返回值String &;
	判断:if( this == & s):避免s=s的意外;
	delete [] str; 删除str之前指向的内容,避免内存泄漏
	str = new char[strlen(s.str)+1]; 重新开辟空间,避免指向同一内存空间
		
String & operator = (const String & s)
{ 
	if( this == & s)
	    return * this;
	delete [] str; 
	str = new char[strlen(s.str)+1]; 
	strcpy( str,s.str);
	return * this;
}

为 String类编写复制构造函数的时候,会面临和 = 同样的问 题,用同样的方法处理。

String( String & s) 
{
   str = new char[strlen(s.str)+1]; 
   strcpy(str,s.str);
}

3. 基类派生类:继承、静态函数、虚析构函数

(!!!相当相当好的一道题 哈哈哈)
class填空,使得输出为:
0 animals in the zoo, 0 of them are dogs, 0 of them are cats;
3 animals in the zoo, 2 of them are dogs, 1 of them are cats;
6 animals in the zoo, 3 of them are dogs, 3 of them are cats;
3 animals in the zoo, 2 of them are dogs, 1 of them are cats

 #include <iostream>
    using namespace std;

// 在此处补充你的代码
    class Animal
    {
    public:
       static int number;
        Animal (){++number;
        cout<<"animal"<<endl;};
      virtual  ~Animal(){--number;cout<<"animal D"<<endl;};
    };
    int Animal::number=0;
      class Dog:public Animal
    {
        public:
        static int number;
         Dog (){++number;
         cout<<"dog"<<endl;};
       ~Dog(){--number;cout<<"dog D"<<endl;};
    };
    int Dog::number=0;
      class Cat:public Animal
    {
        public:
        static int number;
         Cat (){++number;
         cout<<"cat"<<endl;};
        ~Cat(){--number;cout<<"cat D"<<endl;};
    };
    int Cat::number=0;
    void print() {
    	cout << Animal::number << " animals in the zoo, " << Dog::number << " of them are dogs, " << Cat::number << " of them are cats" << endl;
    }
// 在此处结束补充你的代码
    int main() {
    	print();
    	Dog d1, d2;
    	Cat c1;
    	print();
    	Dog* d3 = new Dog();
        Animal* c2 = new Cat;
    	Cat* c3 = new Cat;
    	print();
    	delete c3;
    	delete c2;
    	delete d3;
    	print();
    }

涉及知识点:

  1. number为静态成员变量原因:Animal::number (通过类名访问)
  2. 派生类和基类中同名的静态变量没有任何关系,但是如果派生类继承了基类的静态成员变量,则是一份,内容和地址都相同
  3. 继承的原因:派生类在利用构造函数进行初始化时 ,先执行基类的构造函数;
  4. 基类使用虚析构函数:确保执行正确的析构函数版本(Animal* c2)如果我们要删除的是一个指向派生类对象的基类指针,则需要虚析构函数。
    **ps:**把基类的析构函数声明为virtual
    — >派生类的析构函数可以virtual不进行声明
    —>通过基类的指针删除派生类对象时,首先调用派生类的析构函数,然后调用基类的析构函数
    —>一般来说,一个类如果定义了虚函数,则应该将析构函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数。
    !!!注意:不允许以虚函数作为构造函数

4、C++增强程序可扩充性的办法

①缺省参数的设置:C++允许最右边设置连续的默认参数;
②多态:利用虚函数实现派生类的指针/对象可以赋给基类指针/引用,通过基类指针/引用调用基类和派生类中的同名虚函数时,调用虚函数的类别取决于指向或者引用的对象。

5、多态

①用基类指针数组存放指向各种派生类对象的指 针,然后遍历该数组,就能对各个派生类对象 做各种操作,是很常用的做法

CBase * pBase[100];

②在构造函数和析构函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数。
③派生类中和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数
④多态实现原理:动态联编
“多态”的关键在于通过基类指针或引用调用 一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定。
多态的函数调用语句被 编译成一系列根据基类指 针所指向的(或基类引用 所引用的)对象中存放的虚函数表的地址,在虚函 数表中查找虚函数地址,并调用虚函数的指令。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、纯虚函数与抽象类

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。
在基类中实现纯虚函数的方法是在函数原型后加"=0",例如:

  virtual void func() =0

✔声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。
✔在抽象类的成员函数内可以调用纯虚函数,但是在构造函数析构函数内部不能调用纯虚函数。
✔所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。

7、浮点数比较大小,不能用==

8、函数对象

是个对象,但是用起来像是函数调用,实际上也是函数调用。

class Cmy{
public:
	double operator()(int a1,int a2,int a3)//重载()运算符
	{
			return (double)(a1+a2+a3)/3;
    };
Cmy average;//函数对象
cout<<average(3,2,3);//average.operator()(3,2,3)//用起来看起来像函数调用

深刻理解:

/*
程序填空输出指定结果

输入
多组数据
每组数据两行 

第一行是两个整数 m 和 n
第二行先是一个整数k ,然后后面跟着k个整数



输出

对每组数据,按原顺序输出第二行的后k个整数中,大于m且小于n的数
输出两遍 

数据保证一定能找到符合要求的整数 


输入样例

1 3
1 2
8
5 1 2 3 4 9


输出样例
2,
2,
3,4,
3,4,
*/ 

#include <iostream>
#include <vector>
using namespace std;


struct A {
	int v;
	A() { }
	A(int n):v(n) { };
	bool operator<(const A & a) const {
		return v < a.v;
	}
};

//your code starts here

template <class T>
class FilterClass
{
	private:
		T minV,maxV;
	public:
		FilterClass(T mi,T ma):minV(mi),maxV(ma) { };
		bool operator()(const T & v) const {
			return minV < v && v < maxV;
		}
};
//your code ends here
template <class T>
void Print(T s,T e)
{
	for(;s!=e; ++s)
		cout << *s << ",";
	cout << endl;
}
template <class T1, class T2,class T3>
T2 Filter( T1 s,T1 e, T2 s2, T3 op) 
{
	for(;s != e; ++s) {
		if( op(*s)) {
			* s2 = * s;
			++s2;
		}
	}
	return s2;
}

ostream & operator <<(ostream & o,A & a)
{
	o << a.v;
	return o;
}
vector<int> ia;
vector<A> aa; 
int main()
{
	int m,n;
	while(cin >> m >> n) {
		ia.clear();
		aa.clear(); 
		int k,tmp;
		cin >> k;
		for(int i = 0;i < k; ++i) {
			cin >> tmp; 
			ia.push_back(tmp);
			aa.push_back(tmp); 
		}
		vector<int> ib(k);
		vector<A> ab(k);
		vector<int>::iterator p = 
		 Filter(ia.begin(),ia.end(),ib.begin(),
		 FilterClass<int>(m,n));
		Print(ib.begin(),p);
		vector<A>::iterator pp = Filter(aa.begin(),aa.end(),ab.begin(),
		FilterClass<A>(m,n));
		Print(ab.begin(),pp);
		
	}
	return 0;
}

解读:
Filter是个类模板,
FilterClass是函数模板,

 调用:vector<A>::iterator pp =Filter(aa.begin(),aa.end(),ab.begin(),FilterClass<A>(m,n));//实参m,n,传给构函数,用来初始化。
   FilterClass(T mi,T ma):minV(mi),maxV(ma) { };
    原型:T2 Filter( T1 s,T1 e, T2 s2, T3 op)
       调用:  if( op(*s))//op是函数指针,参数传给()重载那块
	   原型:  bool operator()(const T & v) const

9、如何理解二级指针,怎样正确使用二级指针

就是指针的指针
例如,你想传递指针到函数里(比如,没有头结点的链表),又想把函数中对该指针的改变传递出来(比如,该链表的第一个结点被删除,因此该指针值需要改变),这种情况除了通过函数返回变化后的指针值外,另一个方法就是传递指针的指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值