C++经典题目

构造函数与析构函数的执行顺序

#include <iostream>
using namespace std;
class AA
{
public:
  AA(int i,int j)
  亻
  A = i;
  B =j;
⁤⁤  cout<<"Constructing("<<A<<","<<B<<").\n";⁤⁤
⁤⁤  cout<<"Constructing("<<A<<","<<B<<").\n";⁤⁤
  }
  void print(){cout<<A<<","<<B<<endl;}
private:
  int A. B;
};
void main()
{
⁤  AA *a1,*a2;⁤
  AA a3(3,4),a4(7,8);
  al =new AA(1,2);
  a2=new AA(5,6);
  al->print();
  a3. print();
  a2->print();
  a4. print();
  delete al;
  delete a2;
}
/*运行结果:
  Constructing(3,4).
  Constructing(7,8).
   Constructing(1,2).
  Constructing(5,6).
  1,2
  3,4
  5,6
  7,8
  Destructed(1,2).
  Destructed(5,6).
  Destructed(7,8).
  Destructed(3,4).*/

结果分析:
 本题主要考查构造函数、析构函数的调用时机。
①在定义类对象指针的时候并不会初始化对象,接下来定义类对象后调用构造函数初始化;
②对类指针用new初始化时调用构造函数;
③对指针存储空间的释放需要用delete,这时先调用delete所对应指针对象的析构函数,再根据构造函数执行顺序反序调用对象的析构函数。
  注:先delete那个就先调用其析构函数,不需要根据构造函数执行顺序反序

涉及到继承的情况

#include <iostream>
using namespace std;
class Base
{
public:
	Base(){a=0;}
	Base(int i){ a = i;}
	void print(){cout <<"a="<<a<<",";}
	int Geta(){return a;}
	~Base(){cout <<"Destructor Base a="<<a<<endl;}
private:
	int a;
}; 
class Derived:public Base
{
public:
	Derived(){b=0;}
	Derived(int i,int j,int k):Base(i),Ba(j){b=k;}
	~Derived(){cout<<"Destructor Derived b="<<b<<endl;}
	void print();
private:
	int b;
	Base Ba;
};
void Derived::print()
{
	Base::print();
	cout<<"b="<< b<<",Ba. a="<< Ba. Geta() << endl;
}
int  main()
{
	Derived d1,d2(5,3,7);
	
	d1. print();
	d2. print();
	return 0;
}
/*
a=0,b=0,Ba. a=0
a=5,b=7,Ba. a=3
Destructor Derived b=7
Destructor Base a=3
Destructor Base a=5
Destructor Derived b=0
Destructor Base a=0
Destructor Base a=0
*/

构造函数顺序:先调用继承类的构造函数、再是本类子对象对应类的构造函数、最终是自身构造函数。注:考试的时候不管有没有构造函类的输出,大家最好自己写出构造函数顺序反推机构函数顺序,更保险。

虚继承eg1

#include<iostream>
using namespace std;
class A
{
	int x;
public:
	A(int xx=0):x(xx){cout<<"A constructor.\n";}//一
	~A(){cout<<"A destructor.\n";}
	void output(){cout<<x<<" ";}
	int Getx(){return x;}
};

class B
{
	int y;
	A a;//二
public:
	B(int xx=0,int yy=0):a(xx),y(yy){cout<<"B constructor.\n";}//三
	~B(){cout<<"B destructor.\n";}
	int Gety(){return y;}
	int Geta(){return a. Getx();}
};

class C:public A,virtual public B
{
public:
	C(int x=0,int y=0):A(x),B(x,y){cout<<"C constructor.\n";}//四
	void output()
	{
		A::output();
		cout<<Getx()<<" "<<Gety()<<" "<<Geta()<<endl;
	}
	~C(){ cout<<"C destructor. \n";}
};

int main()
{
	C aa(4,8);
	aa.output();
	return 0;
}
//A constructor.//二
//B constructor.//三
//A constructor.//一
//C constructor.//四
//4 4 8 4
//C destructor.
//A destructor.
//B destructor.
//A destructor.

个人分析:虚继承的构造函数永远是最先执行的,不管各个构造函数出现的顺序如何,编译器总是先调用虚基类的构造函数,再按照出现的顺序调用其他的构造函数。

虚继承eg2

在这里插入图片描述

#include <iostream>
using namespace std;
//虚基类A
class A{
public:
	A(int a);
protected:
	int m_a;
};
A::A(int a): m_a(a){cout<<"A constructor\n"; }
//直接派生类B
class B: virtual public A{
public:
	B(int a, int b);
public:
	void display();
protected:
	int m_b;
};
B::B(int a, int b): A(a), m_b(b){ cout<<"B constructor\n";}
void B::display(){
	cout<<"m_a="<<m_a<<", m_b="<<m_b<<endl;
}
//直接派生类C
class C: virtual public A{
public:
	C(int a, int c);
public:
	void display();
protected:
	int m_c;
};
C::C(int a, int c): A(a), m_c(c){cout<<"C constructor\n";}
void C::display(){
	cout<<"m_a="<<m_a<<", m_c="<<m_c<<endl;
}
//间接派生类D
class D: public B, public C{
public:
	D(int a, int b, int c, int d);
public:
	void display();
private:
	int m_d;
};
D::D(int a, int b, int c, int d): A(a), B(90, b), C(100, c), m_d(d){ cout<<"D constructor\n";}
void D::display(){
	cout<<"m_a="<<m_a<<", m_b="<<m_b<<", m_c="<<m_c<<", m_d="<<m_d<<endl;
}
int main(){
	B b(10, 20);
  	b.display();
  	cout<<endl;
	C c(30, 40);
	c.display();
	cout<<endl;
	D d(50, 60, 70, 80);
	d.display();
	return 0;
}
/*
运行结果:
A constructor
B constructor
m_a=10, m_b=20

A constructor
C constructor
m_a=30, m_c=40

A constructor
B constructor
C constructor
D constructor
m_a=50, m_b=60, m_c=70, m_d=80
*/ 

在最终派生类 D 的构造函数中,除了调用 B 和 C 的构造函数,还调用了 A 的构造函数,这说明 D 不但要负责初始化直接基类 B 和 C,还要负责初始化间接基类 A。而在以往的普通继承中,派生类的构造函数只负责初始化它的直接基类,再由直接基类的构造函数初始化间接基类,用户尝试调用间接基类的构造函数将导致错误。

现在采用了虚继承,虚基类 A 在最终派生类 D 中只保留了一份成员变量 m_a,如果由 B 和 C 初始化 m_a,那么 B 和 C 在调用 A 的构造函数时很有可能给出不同的实参,这个时候编译器就会犯迷糊,不知道使用哪个实参初始化 m_a。

为了避免出现这种矛盾的情况,C++ 干脆规定必须由最终的派生类 D 来初始化虚基类 A,直接派生类 B 和 C 对 A 的构造函数的调用是无效的。在第 50 行代码中,调用 B 的构造函数时试图将 m_a 初始化为 90,调用 C 的构造函数时试图将 m_a 初始化为 100,但是输出结果有力地证明了这些都是无效的,m_a 最终被初始化为 50,这正是在 D 中直接调用 A 的构造函数的结果。

另外需要关注的是构造函数的执行顺序。虚继承时构造函数的执行顺序与普通继承时不同:在最终派生类的构造函数调用列表中,不管各个构造函数出现的顺序如何,编译器总是先调用虚基类的构造函数,再按照出现的顺序调用其他的构造函数;而对于普通继承,就是按照构造函数出现的顺序依次调用的。

参考于C++虚继承时的构造函数的讲解

拷贝构造函数的调用

C++中的拷贝构造函数在下面哪些情况下会被调用()BCD
A、对象创建的时候
B、使用一个类的对象去初始化该类的一个新对象
C、被调用函数的形参是类的对象
D、当函数的返回值是类的对象时,函数执行完成返回调用者
在这里插入图片描述在这里插入图片描述

strlen的详细用法

#include<iostream>
#include<cstring>
using namespace std;
void main()
{
  char s1[]="Hello C++";
⁤  cout<<"sl:"<<s1<<" "<<strlen(s1)<<endl;⁤
  char *s2="string";
  cout<<"s2:"<<s2<<" "<<strlen(s2)<<endl;
⁤  char s3[]="Hello \0backslash C++";⁤
  cout<<"s3:"<<strlen(s3)<<endl;
  cout<<"s4:"<<strlen("asdf ghjk lmnᵐ)<cndl;
  char s5[20];
  cin>>s5;//输入hello world
  cout<<"s5:"<<s5<<" "<<strlen(s5)<<endl;}
//s1:Hello C++ 9
//s2:string 6
//s3:6
//s4:13
//s5:hello 5//因为空格相当于结束输入

结果分析:
  本题主要考查字符串的概念。在自定义的字符串中,‘\0’为字符串结束的标志,如果在’\0’之后还有内容,不管后面是什么均被忽略,因此在本题中s3的长度仅为’⁤hello^{,}⁤的长度5。对比之下,如果是输入的字符串的话,遇到空格或指标符后,只将第一个空格或制表符前的字符串赋值,后面的均不算入。

表达式优先级集锦

#include<iostream>
using namespace std;
int main()
{
	int a,b,c,x,y;
	a = 1;
	b=2;
	c = 0;
	cout << a++-1 <<endl;
	cout << (a && b || !c) << endl;
	cout << b/++a << endl;
	cout <<a<<" "<<b<<" "<<c<<endl;
	x = ++a || ++b && ++c;
	cout <<a<<" "<<b<<" "<<c<<" "<<x<<" "<<endl;
	a = b = c = -1;
	y = ++a && ++b && ++c;
	cout <<a<<" "<<b<<" "<<c<<" "<<y<<" "<<endl;
	return 0;
}
/*
0
1
0
3 2 0
4 2 0 1
0 -1 -1 0
*/

运行结果:
结果分析:
  ①执行a+±1时先计算a-1为0,输出0,此时a自增1变为2
  ②从左到右顺次执行a&&b||!c,a为2非0,b为2非0,因此a&&b为真。故||左端为真,因此整个表达式为真,值为1,输出1。
  ③b/++a中,先执行a自增1操作变为3,继续运算b/a即2/3结果为0,输出0。
  ④这行代码中的运算符优先级顺序是:++,&&,||,=。所以,这行代码的执行顺序是:
++a:先执行自增运算,将变量 a 的值加 1。
++b && ++c:然后执行逻辑与运算。如果 ++b 的值为真,则执行 ++c,将变量 c 的值加 1。
++a || (++b && ++c):接着执行逻辑或运算。如果 ++a 的值为真,则不再执行 ++b && ++c。
x = ++a || (++b && ++c):最后执行赋值运算,将 ++a || (++b && ++c) 的结果赋给变量 x。
  ⑤abc均被赋为-1 后,执行y=++a&&++b&&++c;, 首先a自增为0,此时已经能判断y的值为0,故++b、++c被逻辑短路,此时b=c=-1。这时,a=0,b=-1,c=-1,y=0。
  关于优先级可以看我的另一篇文章C++重载和运算符优先级

析构函数的执行顺序

#include<iostream>
using namespace std;
class A
{
  double d;
public:
  A(doublei=0){d=i;}
  void Print()
  {
  cout<<"Constructor"<<d<<endl;
  cout<<"d="<<d<<endl;
 }
  ~A(){cout<<"Destructor"<<d<<endl;}};
void main()
{
  A a;
  a=3.693;
  a. Print();
}
/*
Destructor3.693
Constructor3.693
d=3.693
Destructor3.693
*/

结果分析:
  本题重点在于赋值表达式语句a=3.693;,赋值表达式等号左边是类A的对象,右边是一个double类型的值。左右值虽然不同但是在C++编译器里面却是合法的。因为:类A中具有double类型的构造函数,这样的赋值表达式是将自动调用该参数的构造函数,首先将double类型的值3.693转化为A的临时对象,然后返回,这样左右均是A的对象便可进行赋值操作了。因此第一行的析构是给A转化的临时对象被释放。
  
临时对象什么时候被释放?
  一般来说,C++ 中的临时变量在表达式结束之后 (full expression) 就被会销毁。但也有例外的时候,如果这个临时变量被用来初始化一个引用的话,那这个临时变量的生命周期就会被延长,直到引用被销毁,从而不会因此产生悬空 (dangling)的引用。

求最大公约数

程序简单,但是十分有用的求最大公约数,需要记住

#include <iostream>
using namespace std;
int max_num(int a,int b)
{
	while(a != b)
	{
		while(a > b)
			a = a-b;
		while(b > a)
			b = b-a;
	}
	return a;
}
int main()
{
	int a,b;
	cin >> a >> b;
	cout << "最大公约数:" << max_num(a,b) << endl;
	return 0;
}

求数组中不重复的元素

//输出数组中不重复的元素 
#include <iostream>
using namespace std;

int main()
{
	int a[10]={1,2,2,2,3,4,4,5,1,5};
	int n=0;//用于记录不重复的元素
	int i,j,k,c;
	
	for(i=0;i<10-n;i++)
	{
		c=a[i];
		for(j=i+1;j<10-n;j++)
		{
			if(a[j] == c)
			{
				for(k=j;k<10-n;k++)
				{
					a[k]=a[k+1];	
				}
				n++;
				j--;
			}
		}
	 } 
	 cout<<n<<endl;
	 for(i=0;i<10-n;i++)
	 	cout<<a[i]<<" ";
	cout<<endl;
	return 0;
}
//输出:
//5
//1 2 3 4 5

求质数

#include <iostream>
using namespace std;
const int MAX_LEN=20;
int p(int x,int a[])//将[2,x]内的所有质数输出到a数组中
{
	int i,j,k=0;
	for(i = 2;i <= x;i++)
	{
		for(j = 2;j <= i;j++)
		{
			if(!(i%j))
				break;
		}
		if(i == j)
			a[k++] = i;
	} 
	return k;//返回的是质数数组的长度 
} 

int  main()
{
	int m[MAX_LEN];
	int q=p(13,m);
	cout<<"count:"<<q<<endl;
	for(int i = 0;i<q;i++)
		cout<<m[i]<<" ";
	
	return 0;
}
/*
count:6
2 3 5 7 11 13
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SODA_BLUEBLUE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值