文章目录
一个空类有哪些默认的函数?
默认构造函数、默认拷贝构造函数、默认析构函数、默认赋值运算符、取址运算符和 取址运算符const。总共有六个函数。一个示例如下:
Class Empty{
Empty();
Empty(const Empty&);
~Empty();
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
但是,C++默认生成的函数,只有在被需要的时候,才会产生。即当我们定义一个类,而不创建类的对象时,就不会创建类的构造函数、析构函数等。
如果类重载了构造函数,还会有默认的构造函数吗?
不会。
赋值构造函数和拷贝构造函数的区别?
拷贝构造函数是在对象创建时调用的,赋值函数只能被已经存在的对象进行调用。
String a(“hello”);
String b(“world”);
String c=a; //调用拷贝构造函数
c=b; //调用了赋值构造函数
拷贝构造函数和赋值构造函数不同的地方还在于赋值构造函数在复制新值以前需要删除旧值。但是如果自己等于自己的时候,就不能删除原来的值,需要返回this指针。
深拷贝和浅拷贝的区别?
在对象拷贝过程中,如果没有自定义拷贝构造函数,编译器会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量则调用其相应的拷贝构造函数。
浅拷贝存在三个错误:
- 原有的内存没释放
- 两个指针指向一个内存,互相影响
- 一块内存同时释放了两次
重载 前++和后++的区别?
UPInt& UPInt::operator++() //返回值为引用类型
{
*this += 1;
return *this; // 或return ++privateVal;
}
//后置 i++ -----------先调用原值,再++
const UPInt UPInt::operator++(int) //参数为一个int型,返回值为const对象
{
UPInt oldValue = *this;
++(*this);
return oldValue;
}
构造函数和析构函数可以是虚函数吗?
- 构造函数不可以为虚函数,因为虚函数表指针就是在构造函数中建立的,如果构造函数为虚函数,无法保证虚函数指针的建立。
- 虚析构函数的地址存在于虚函数表中,和普通虚函数别无二致,同时也会像普通的虚函数一样进行覆盖,虽然父子的析构函数名字不一样,但是他们占同一个坑(即父子析构函数在虚函数表中的位置是一样的,否则就不存在多态了)。析构时,到特定的坑中调用该类型的析构函数,其析构函数中又嵌套了很多对父类的析构函数的调用。
c与c++区别,介绍面向对象特性?
- C++是面向对象的语言,C语言是面向函数的语言。
- C语言不支持函数重载。
- C语言中常见的struct和C++常见的class除了访问默认权限不同,别的功能几乎相同。
- 封装继承多态
面向对象理解
封装、继承、多态
类的继承、虚继承、虚基类、抽象类?
类的继承/派生类/基类:
- 构造方法用来初始化类的对象,与父类的其它成员不同,它不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。
- 如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建。
- 子类没有自定义构造函数,系统会给子类默认无参构造函数,并且调用父类的无参构造。子类自定义了构造函数,先检查有没有调用父类的构造函数,没有的话就调用了父类的无参构造。
多继承存在的问题:
- 浪费存储空间;
- 存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。
虚函数和虚继承的实现原理:
- 他们有相似之处,都利用了虚指针(对象)和虚表(类)。
- 虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。
- 虚函数父类和子类都有虚函数表,而虚继承只有子类有虚继承表。
- 类的虚继承表是会被继承到子类的,虚函数不会被继承。但是在多重继承中,派生类会有多个虚函数表。
抽象类:
- 包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。
- 抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。
类的多态是怎么实现的?
- 继承的目的是为了代码复用,而多态的目的是为了实现接口复用。
- 类的多态包括 静态多态(函数重载、泛式编程)以及 动态多态(虚函数)。
静态多态,根据传入不同的参数(个数或者类型不同)调用不同的实现。
动态多态,不论传过来的是哪个类的对象,函数都能通过同一接口调用到各自对象的实现的方法。
动态联编:为每一个包含虚函数的建立一个虚函数表,虚函数表的每一个表项存放的是虚函数在内存中的入口地址。
在该类的每个对象中设置一个指向虚函数表的指针,在调用虚函数时,先采用虚指针找到虚函数表,确定虚函数的入口地址在表中的位置,然后获取入口地址完成调用。
虚函数表是共有的吗?
一个类一个独立享有一张虚寒函数表,同一个类的不同对象的虚函数指针指向同一个位置。
虚函数是按照什么顺序来放的?
①虚函数有两个来源,一个是自己定义的,一个是继承父类的。
②所有虚函数排序,继承于父类的虚函数都放前面,新声明函数放在后面。
③确认虚函数的入口地址,继承父类的虚函数,如果被改写了,那么放的就是改写之后的地址,新声明的虚函数的地址就是在当前类的地址。
问题三:多重继承中,派生类有多少张虚函数表?
多重继承了几次,就有多少张虚函数表。
class A{
virtual fun();
};
class B{
virtual fun();
};
class C:public B,A{
virtual fun();
};
int main(int argc, char* argv[])
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
return 0;
}//4 4 8
public、private、protect继承后属性发生什么变化?
访问范围 | public | private | protected |
---|---|---|---|
类里面 | yes | yes | yes |
实例化的对象 | yes | no | no |
友元函数 | yes | yes | yes |
共有继承以后 | yes | yes | yes |
从基类继承过来的成员在子类中(对子类内的成员)访问权限的变化情况
基类(父类) | public | protected | private |
---|---|---|---|
公有继承 | public | protected | 不可见 |
保护继承 | protected | protected | 不可见 |
私有继承 | private | private | 不可见 |