DDL
- 1.题目及分析
- 1.对象数组的析构顺序
- 2.浅拷贝的隐患
- delete p 还是 delete[]p ?
- 类似的题,自行查阅
- 3.常数据成员的初始化
- 4.默认构造函数
- 5.cin、cout所属类
- 6.重载
- 7.静态数据成员
- 8.多态
- 8.联编
- 9.内联函数
- 10.引用
- 11.static
- 12.构造Complex类
- 13.静态成员函数
- 14.抽象类
- 15.标识符
- 16.指针数组
- 17.不可嵌套定义,可嵌套调用
- 18.可见性 、存在性
- 19.虚函数
- 20.子类重写虚函数的权限
- 21.虚析构函数
- 22.重载、覆盖
- 23.继承
- 24.类与对象
- 25.友元关系
- 25.公有继承
- 26.多态
- 27.
- 28.面向对象
- 29.四要素
- 30嵌套
- 2.更新日志
1.题目及分析
1.对象数组的析构顺序
#include <iostream>
using namespace std;
class T {
public:
~T() { cout << "Destroying..." << i << endl; }
void Setij(int a, int b) { i = a, j = b; }
int GetMuti() { return i * j; }
protected:
int i, j;
};
int main()
{
T* p = new T[5];
for (int j = 0; j < 5; ++j) p[j].Setij(j, j);
for (int k = 0; k < 5; ++k) cout << "Multi[" << k << "] is:" << p[k].GetMuti() << endl;
//程序结束并不会隐式地调用析构函数,需要显式调用析构函数
//析构顺序与构造顺序相反
delete[]p;
return 0;
}
2.浅拷贝的隐患
#include <iostream>
using namespace std;
class Vector {
public:
Vector(int s = 100);
~Vector();
int& Elem(int idx);
void Display();
void Set();
protected:
int size;
int* buffer;
};
Vector::Vector(int s)
{
buffer = new int[size = s]; //巧妙1: 初始化size的同时也初始化了buffer
}
Vector::~Vector()
{
cout << "扫尾工作..." << endl;
delete []buffer;
}
int& Vector::Elem(int idx)
{
if (idx < 0 || idx >= size)
{
cout << "error in index" << endl;
exit(-1);
}
return buffer[idx];
}
void Vector::Display()
{
for (int i = 0; i < size; ++i)
cout << Elem(i) << endl;
}
void Vector::Set()
{
for (int i = 0; i < size; ++i)
Elem(i) = i + 1; //巧妙2: 利用返回值为引用,直接修改了buffer(i)
}
int main()
{
Vector a(10);
Vector b(a);
a.Set();
b.Display();
return 0;
}
可以很清楚看到浅拷贝所造成的错误:在析构的时候会重复析构,这是由于浅拷贝时,b的buffer指针和a的buffer指针指向的是同一片空间
如何更改?
换为深拷贝!
即弃用默认拷贝构造函数,自己写一个拷贝构造函数
#include <iostream>
using namespace std;
class Vector {
public:
Vector(int s = 100);
Vector(const Vector& v); //拷贝构造函数
~Vector();
int& Elem(int idx);
void Display();
void Set();
protected:
int size;
int* buffer;
};
Vector::Vector(int s)
{
buffer = new int[size = s]; //巧妙1: 初始化size的同时也初始化了buffer
}
Vector::Vector(const Vector& v)
{
//照猫画虎 重新开辟空间,使得通过调用拷贝构造函数创建的对象的buffer指向另一片空间
this->buffer = new int[this->size = v.size];
//拷贝数据 [也可不加this指针进行访问]
for (int i = 0; i < size; ++i)
buffer[i] = v.buffer[i];
}
Vector::~Vector()
{
cout << "扫尾工作..." << endl;
delete []buffer;
}
int& Vector::Elem(int idx)
{
if (idx < 0 || idx >= size)
{
cout << "error in index" << endl;
exit(-1);
}
return buffer[idx];
}
void Vector::Display()
{
for (int i = 0; i < size; ++i)
cout << Elem(i) << endl;
}
void Vector::Set()
{
for (int i = 0; i < size; ++i)
Elem(i) = i + 1; //巧妙2: 利用返回值为引用,直接修改了buffer(i)
}
int main()
{
Vector a(10);
Vector b(a);
a.Set();
b.Display();
return 0;
}
改为深拷贝后,a、b对象不会相互影响,由于b未调用set()函数,所以其buffer里面的数据仍为随机值
delete p 还是 delete[]p ?
#include <iostream>
using namespace std;
int main()
{
int* p1 = new int(5); //只分配一个int类型的空间,并初始化为5
cout << *p1 <<" "<< p1[0] << endl;
delete p1;
int* p2 = new int[5]; // 指向数组
p2[0] = 1;
p2[1] = 10;
cout << *p2 << " " << p2[0] << " " << p2[1] << endl;
delete[] p2; //依次销毁p2所指向的完整完整空间
return 0;
}
delete []p 是指 释放p所指向的完整空间
记清楚两者的适用条件即可
类似的题,自行查阅
3.常数据成员的初始化
常数据成员名只可在初始化列表中进行
(上机在VS2019尝试后,发现在定义时初始化也能编译通过,但并不建议这样做)
4.默认构造函数
默认构造函数(default constructor)就是在没有显式提供初始化式时调用的构造函数。
它由不带参数的构造函数,或者为所有的形参提供默认实参的构造函数定义
①
class T{
public:
T(){}
protected:
int x;
}
②
class T{
public:
T(int xx = 0) {}
protected:
int x;
}
5.cin、cout所属类
B
cin 属于 istream类
cout 属于 ostream类
6.重载
A
类模板不可以重载,但是函数模板可以重载。
7.静态数据成员
静态数据成员指的是 static修饰的数据成员
A
8.多态
A
多态分为4类: 重载多态 、 强制多态、参数多态、包含多态
前两种又被称为专用多态
后两种又被称为通用多态
前三种的绑定工作在编译连接阶段即可完成
包含多态的绑定工作在程序运行阶段才可完成
8.联编
B
多态分为静态多态(编译时多态、静态联编、早绑定)和动态多态(运行时多态、动态联编、晚绑定)
9.内联函数
正确
10.引用
❌
11.static
static修饰的数据成员为该类的所有对象_共享___
12.构造Complex类
#include <iostream>
using namespace std;
class Complex {
public:
Complex(double rr = 0.0 ,double ii = 0.0):r(rr),i(ii){}
void show() { cout << r << " " << i << endl; }
Complex operator+(const Complex& c) {
return Complex(this->r + c.r, this->i + c.i);
}
private:
double r, i;
};
int main()
{
Complex c1(-1, 4), c2(2, 5);
c1.show();
c2.show();
Complex c3 = c1 + c2;
c3.show();
return 0;
}
13.静态成员函数
B
#include <iostream>
using namespace std;
class T {
public:
static void show_x() { return this->x; } //错误,this指针只存在于非静态函数内部
private:
static int x;
};
int T::x = 1; //静态数据成员类外初始化
int main()
{
return 0;
}
14.抽象类
#include <iostream>
using namespace std;
#include <algorithm>
const double PI = acos(-1);
class CShape {
public:
virtual double GetLength() const = 0;
};
class CSqure :public CShape {
public:
CSqure(double x, double y):x(x),y(y){}
double GetLength()const { return 2 * (x + y); }
private:
double x, y;
};
class CCircle :public CShape {
public:
CCircle(double r):r(r){}
double GetLength()const { return 2 * PI * r; }
private:
double r;
};
int main()
{
CSqure c1(1, 2);
cout << c1.GetLength() << endl;
CCircle c2(1);
cout << c2.GetLength() << endl;
return 0;
}
15.标识符
16.指针数组
17.不可嵌套定义,可嵌套调用
18.可见性 、存在性
19.虚函数
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
20.子类重写虚函数的权限
#include <iostream>
using namespace std;
class B {
public:
B(){}
B(int i) { b = i; }
virtual void virfun()
{
cout << "B::virfun() called.\n";
}
private:
int b;
};
class D :public B {
public:
D(){}
D(int i, int j) :B(i) { d = j; }
private:
int d;
void virfun()
{
cout << "D::virfun() called.\n";
}
};
void fun(B* obj)
{
obj->virfun();
}
int main()
{
D* pd = new D;
fun(pd);
return 0;
}
虽然在子类中声明了private,但其实仍然可以调用虚函数
虚函数编译时的访问权限仅仅是和调用者(指针、引用)的类型有关,编译器只要知道这个类型有一个可以访问的虚函数就行了,并不查看具体对象类型中该虚函数的访问权限
简而言之,基类是public权限即可成功调用虚函数
21.虚析构函数
22.重载、覆盖
23.继承
24.类与对象
25.友元关系
A
友元关系破坏了封装性,可提高程序的运行效率
25.公有继承
C
只可访问公有成员
26.多态
27.
28.面向对象
面向对象将数据和对数据的操作作为一个相互依赖,不可分割的整体,采用了数据抽象和信息隐蔽技术。
29.四要素
抽象性、封装性、继承性和多态性
30嵌套
在C++中,函数的定义不可以嵌套,类的定义可以嵌套。
2.更新日志
2022.11.19 整理
欢迎交流、讨论、指正~
不正确、不理解之处欢迎评论留言~