构造函数及其初始化列表顺序
int init(const std::string & info)
{
std::cout << info << std::endl;
return 0;
}
class A
{
int m_x;
public:
A():m_x(init("Init A::m_x"))
{init("Call A::A()");}
};
class A1:public A
{
int m_y;
int m_x;
public:
A1():m_x(init("Init A1::m_x")),
m_y(init("Init A1::m_y"))
{init("Call A1::A1()");}
};
int main()
{
A1* a1 = new A1;
}
执行结果为
Init A::m_x
Call A::A()
Init A1::m_y
Init A1::m_x
Call A1::A1()
考察构造函数的顺序。大家需要注意的是,构造函数的执行顺序和初始化序列表中的顺序无关,而是和声明的顺序相关,
如果我们A1的构造函数修改为
A1():m_y(init("Init A1::m_y")),
m_x(init("Init A1::m_x")),答案仍为A,因为m_x的声明比m_y先。父类的构造顺序的规则相同。
class Base
{
public:
Base(int val = 0):m_x(val){}
Base(const Base& oth):m_x(oth.m_x){}
private:
int m_x;
};
class Derived:public Base
{
public:
Derived(int val):Base(val), m_y(val){}
Derived(const Derived& oth):m_y(oth.m_y){}
private:
int m_y;
};
int main()
{
Derived d1(10);
Derived d2 = d1;
return 0;
}
d1.m_x = 10 d1.m_y = 10
d2.m_x = 0 d2.m_y = 10
考察拷贝构造函数。子类的构造函数因为没有显示的在初始化序列化表里调用父类的考别构造函数,编译器默认就调用父类的没有参数的构造函数,即调用
父类的Base(int val = 0);
class Base
{
protected:
int m_a;
public:
Base(int a):m_a(a){}
};
class Derived:public Base
{
int m_b;
int m_c;
public:
Derived (int a, int c):m_c(c), Base(a), m_b(m_a+m_c){}
};
int main()
{
Derived a1(1,10);
return 0;
}
a1.m_a、a1.m_b、a1.m_c分别为1、-858993459、10
原因可见题37的解析,m_b比m_c先初始化,所以初始化m_b时,m_c的值是不确定的,所以m_b是不确定的,最后mc初始化后才确定。
基类数据成员总是在派生类数据成员之前被初始化,所以使用继承时,要把基类的初始化列
在成员初始化列表的最前面。(如果使用多继承,基类被初始化的顺序和它们被派生类继承
的顺序一致,它们在成员初始化列表中的顺序会被忽略。
可以正确的初始化是
Derived (int a, int c):m_c (m_a+m_b), m_b(c),Base(
而且初始化顺序可以变化
Derived (int a, int c): Base(a) m_c(m_a+m_b), m_b(c),{}
此时
a1.m_a、a1.m_b、a1.m_c分别为1、10、11
为什么拷贝构造函数必须是引用?
#include <iostream>
class Base
{
public:
Base(){std::cout<<" Base constructor is called!"<<std::endl;}
Base(const Base& bsase) //拷贝构造函数
{std::cout<<"Base copy constructor is called!"<<std::endl;}
};
int main(){
Base base1;
Base bas2(base1); //创建新对象的时候调用
//结果:Base constructor is called!
// Base copy constructor is called!
return 0;
}
拷贝构造函数必须是const类型的引用吗?
答:拷贝构造函数是用一个已有的对象初始化一个新建的对象,把已有对象成员变量一次赋值给新对象的成员变量,原有对象不应该被改变,因此使用const类型。但是使用非const类型,仍然可以实现其功能。
拷贝构造函数必须是引用类型吗?
答:拷贝构造函数必须是引用类型,否则拷贝构造函数会循环调用。当拷贝构造函数使用值传递的结果如下。
class Base
{
public:
Base(){std::cout<<" Base constructor is called!"<<std::endl;}
Base(const Base bsase) //拷贝构造函数
{std::cout<<"Base copy constructor is called!"<<std::endl;}、
};
int main()
{
Base base1;
Base bas2(base1); //创建新对象的时候调用
//使用值传递,会新建一个Base类型的临时对象temp1
//base1 = temp1;
//bas2(temp1);
//temp1 = temp2;
//bas2(temp2);
// ....无限循环
return 0;
}
基类析构函数为什么要声明为virtual?
成员函数为虚函数用指针和引用调用时是动态解析(动态绑定),在函数运行时决定其状态。对象和成员函数是静态解析,在代码编译时函数的状态已经确定。
#include <iostream>
class Base
{
public:
Base(int _i):i(_i)
{std::cout<<"Base constructor is called!"<<std::endl;}
~Base(){std::cout<<"Base }destructor is called!"<<std::endl;}
void fun(){std::cout<<"Base::fun is called!"<<std::endl;}
private:
int i;
};
class Drived:public Base
{
public:
Drived(int _i): Base(_i)
{std::cout<<"Drived constructor is called!"<<std::endl;}
~Drived(){std::cout<<"Drived }destructor is called!"<<std::endl;}
void fun(){std::cout<<"Drived::fun is called!"<<std::endl;}
private:
int i;
};
int main() {
Base base(5);
Drived drived(5);
base = drived;
Base* pb = &drived;
Base &rb = drived;
base.fun(); //Base::fun is called!
pb->fun(); //Base::fun is called!
rb.fun(); //Base::fun is called!
// 全部为静态解析
return 0;
}
#include <iostream>
class Base
{
public:
Base(int _i):i(_i)
{std::cout<<"Base constructor is called!"<<std::endl;}
~Base(){std::cout<<"Base }destructor is called!"<<std::endl;}
virtual void fun(){std::cout<<"Base::fun is called!"<<std::endl;}
private:
int i;
};
class Drived:public Base
{
public:
Drived(int _i): Base(_i)
{std::cout<<"Drived constructor is called!"<<std::endl;}
~Drived(){std::cout<<"Drived }destructor is called!"<<std::endl;}
void fun(){std::cout<<"Drived::fun is called!"<<std::endl;}
private:
int i;
};
int main() {
Base base(5);
Drived drived(5);
base = drived;
Base* pb = &drived;
Base &rb = drived;
base.fun(); //Base::fun is called! 静态解析
pb->fun(); //Drived::fun is called! 动态解析
rb.fun(); //Drived::fun is called! 动态解析
// 全部为静态解析
return 0;
}
为什么基类析构函数必须是虚函数?
#include <iostream>
class Base
{
public:
Base(int _i):i(_i)
{std::cout<<"Base constructor is called!"<<std::endl;}
//~Base(){std::cout<<"Base }destructor is called!"<<std::endl;}
//析构函数为非虚函数,静态绑定。delete基类的指针,只会调用基类的析构函数
//Base constructor is called!
//Drived constructor is called!
//Base destructor is called!
virtual ~Base(){std::cout<<"Base destructor is called!"<<std::endl;}
//析构函数为虚函数,d动态绑定。
//delete基类的指针,调用派生类的析构函数,派生类的析构函数自动调用基类的析构函数
//结果:Base constructor is called!
//Drived constructor is called!
//Drived destructor is called!
//Base destructor is called!
virtual void fun(){std::cout<<"Base::fun is called!"<<std::endl;}
private:
int i;
};
class Drived:public Base
{
public:
Drived(int _i): Base(_i)
{std::cout<<"Drived constructor is called!"<<std::endl;}
~Drived(){std::cout<<"Drived destructor is called!"<<std::endl;}
void fun(){std::cout<<"Drived::fun is called!"<<std::endl;}
private:
int i;
};
int main() {
//Base base(5);
//Drived drived(5);
//base = drived;
//Base* pb = &drived;
//Base &rb = drived;
Base* pb = new Drived(3);
//base.fun(); //Base::fun is called! 静态解析
//pb->fun(); //Drived::fun is called! 动态解析
//rb.fun(); //Drived::fun is called! 动态解析
delete pb;
// 全部为静态解析
return 0;
}
什么时候把析构函数声明为虚函数?虚函数的缺点是什么
函数声明为虚函数需要生成虚函数表,占用内存,影响效率。只有当一个类为基类时才会有需要。只有当class内至少有一个virtual函数时,才需要把析构函数声明为虚函数。
编译器会默认生成哪些函数?
编译器会默认生成:构造函数(当显式定义了构造函数,编译器不会合成默认构造函数,如果需要无参构造函数,需要显式定义),拷贝构造函数,拷贝赋值函数,析构函数。
#include <iostream>
class Base
{
public:
Base(int _i):i(_i)
{std::cout<<"Base constructor is called!"<<std::endl;}
~Base(){std::cout<<"Base destructor is called!"<<std::endl;}
private:
int i;
//int i&;当成员变量是引用时,编译器的自带的赋值运算符会失效
//int const i;当成员变量是const类型时,编译器自带的赋值运算符会失效
};
int main(){
Base base1(1);
Base base2(2);
base1 = base2;
return 0;
}
- 基类和派生类成员变量可以重名吗?
class Point2d {
public:
void print_x0() {cout << x0 << endl;}
void print_x() {cout << x << endl;}
void print_y() {cout << y << endl;}
long long x0 = 0;
private:
long long x = 1;
long long y = 2;
};
class Point3d : public Point2d {
public:
// constructor(s)
// operators
// access functions
void print_x0() {cout << Point2d::x0 << endl;}
void print_x() {cout << x << endl;}
void print_y() {cout << y << endl;}
void print_z() {cout << z << endl;}
private:
long long x = 11;
long long y = 12;
long long z = 13;
};
int main()
{
Point2d d2;
Point3d d3;
d2.print_x0(); // 0
d2.print_x(); // 1
d2.print_y(); // 2
d3.print_x0(); // 0
d3.print_x(); // 11
d3.print_y(); // 12
d3.print_z(); // 13
Point2d *d22 = (Point2d *)&d3;
d22->print_x(); // 1
}