c++面向对象三大特征封装、继承和多态知识总结

C++学习——面向对象三大特性:封装、继承、多态 今天来更新面向更新的三大特性,分别是封装、继承、多态,是C++面试中常考也是非常重要的面试题之一,如果最近有春招的宝可以关注我噢,我会定时分享C++相关常考的知识与面试题哒! 阅读详情

面向对象三大特征:封装,继承,多态;

一、封装:该公开的就公开话,该私有的就隐藏掉,主要是由public,private实现;作用是便于分工和分模块,防止不必要的扩展;

二、继承:就是一种传承,可以把父类型中的数据传承到子类中,子类除了传承了父类的数据之外,还可以对父类型进行扩展;

公开继承  public

保护继承  protected

私有继承  private

保护成员:在子类和本类中可以访问,其他不行;


1、公开继承:在公开继承下,父类型中的数据是公开的到子类型中权限是公开的;父类型中保护权限的数据到子类中是保护的;父类中私有的数据到子类中会隐藏掉(就是说看不见权限,但是实际上式在子类中的);

2、私有继承:在私有继承下,父类中的公开数据到子类中变成私有的,父类中的保护数据到子类中称为私有的,父类中的私有数据到子类中隐藏;

3、保护继承:保护继承下,父类中的公开数据和保护数据到了子类中都成为保护权限,父类中私有的数据到了子类中就变成了隐藏的;


4、注意:不管何种类型的继承关系,父类私有成员到子类中都成为了隐藏掉。

公开继承下的public成员和protected成员的权限变化:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class A{  
  5. public:  
  6.     int a;  
  7.     int geta(){  
  8.         a = 300;  
  9.         return a;  
  10.     }  
  11.     /*保护类型成员在本类和子类中可以访问*/  
  12. protected:  
  13.     int b;  
  14. private:  
  15.     int c;  
  16. };  
  17.   
  18. class B:public A{  
  19.     //int x;  
  20. public:  
  21.     void getb(){  
  22.         b = 200;  
  23.     }  
  24.     void show(){  
  25.         cout << b << endl;  
  26.     }  
  27.     void showa(){  
  28.         cout<< a<<endl;  
  29.     }  
  30. };   
  31. /*关键在于如何设置接口,成功合理的访问到各种类型的数据*/  
  32. int main(){  
  33.     B pex;  
  34.     /*公开继承public成员依旧是public,所以可以类外访问*/  
  35.     pex.a = 100;  
  36.     /*b是保护类型成员,可以通过设置public接口来访问*/  
  37.     pex.getb();  
  38.     pex.show();  
  39.     /*隐藏成员的问题,怎么访问到隐藏的成员*/  
  40.     pex.geta();  
  41.     pex.showa();  
  42.     //A a = pex;//子类类型赋给了父类类型  
  43.     //a.geta();  
  44.     //cout << a.a << endl;  
  45. }  
#include <iostream>
using namespace std;

class A{
public:
	int a;
	int geta(){
		a = 300;
		return a;
	}
	/*保护类型成员在本类和子类中可以访问*/
protected:
	int b;
private:
	int c;
};

class B:public A{
	//int x;
public:
	void getb(){
		b = 200;
	}
	void show(){
		cout << b << endl;
	}
	void showa(){
		cout<< a<<endl;
	}
}; 
/*关键在于如何设置接口,成功合理的访问到各种类型的数据*/
int main(){
	B pex;
	/*公开继承public成员依旧是public,所以可以类外访问*/
	pex.a = 100;
	/*b是保护类型成员,可以通过设置public接口来访问*/
	pex.getb();
	pex.show();
	/*隐藏成员的问题,怎么访问到隐藏的成员*/
	pex.geta();
	pex.showa();
	//A a = pex;//子类类型赋给了父类类型
	//a.geta();
	//cout << a.a << endl;
}


5、私有继承下的成员的权限变化关系,注意如何设置合理的 访问接口去访问 隐藏的数据成员。

  1. /*私有继承下的权限变化,关键是设置合理的接口访问 
  2. 父类中的各种类型的数据成员*/  
  3. #include <iostream>  
  4. using namespace std;  
  5.   
  6. class A{  
  7. private:  
  8.     void showa(){  
  9.         cout << "this is showa()" << endl;  
  10.     }  
  11. protected:  
  12.     void showb(){  
  13.         cout << "this is showb" << endl;  
  14.     }  
  15. public:  
  16.     void showc(){  
  17.         cout << "this is showc" << endl;  
  18.     }  
  19.     void geta(){//设置合理的接口访问A中的私有数据  
  20.         showa();  
  21.     }  
  22. };  
  23.   
  24. class B:private A{  
  25. public:  
  26.     void show(){  
  27.         showc();  
  28.         showb();  
  29.         geta();  
  30.     }  
  31. };  
  32.   
  33. int main(){  
  34.     B b;  
  35.     //A a = b;对比公开继承,对比一下  
  36.     b.show();  
  37. }  
/*私有继承下的权限变化,关键是设置合理的接口访问
父类中的各种类型的数据成员*/
#include <iostream>
using namespace std;

class A{
private:
	void showa(){
		cout << "this is showa()" << endl;
	}
protected:
	void showb(){
		cout << "this is showb" << endl;
	}
public:
	void showc(){
		cout << "this is showc" << endl;
	}
	void geta(){//设置合理的接口访问A中的私有数据
		showa();
	}
};

class B:private A{
public:
	void show(){
		showc();
		showb();
		geta();
	}
};

int main(){
	B b;
	//A a = b;对比公开继承,对比一下
	b.show();
}


6、突破成员访问权限,可以设置合理的访问接口,也可以使用友元类。

  1. /*友元类*/  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. class A{  
  6. private:  
  7.     int x;  
  8.     int y;  
  9. public:  
  10.     A():x(10),y(123){}  
  11.     /*B,C声明为A的友元类之后,可以访问到父类的所有类型成员*/  
  12.     friend class B;  
  13.     friend class C;  
  14. };  
  15.   
  16. class B:public A{  
  17. public:  
  18.     void show(){  
  19.         cout << x << "---" << y << endl;   
  20.     }  
  21. };  
  22.   
  23. class C{  
  24. public:  
  25.     void show(){  
  26.         A a;  
  27.         cout <<a.x<< "---" << a.y << endl;  
  28.     }  
  29. };  
  30.   
  31. int main(){  
  32.     B b;  
  33.     b.show();  
  34.     C c;  
  35.     c.show();  
  36. }  
/*友元类*/
#include <iostream>
using namespace std;

class A{
private:
	int x;
	int y;
public:
	A():x(10),y(123){}
	/*B,C声明为A的友元类之后,可以访问到父类的所有类型成员*/
	friend class B;
	friend class C;
};

class B:public A{
public:
	void show(){
		cout << x << "---" << y << endl; 
	}
};

class C{
public:
	void show(){
		A a;
		cout <<a.x<< "---" << a.y << endl;
	}
};

int main(){
	B b;
	b.show();
	C c;
	c.show();
}


7、继承中构造函数、析构函数、赋值运算符函数和拷贝构造函数

构造函数和析构函数是不能被继承的,但是可以被调用。并且子类一定会调用父类的构造函数;

子类默认调用父类的无参构造,也可以制定调用构造函数;

析构函数的调用和构造函数的调用顺序相反;

拷贝构造函数和赋值运算符函数也不能被继承:在子类不提供拷贝构造和赋值运算符时,子类默认调用父类的赋值运算符和拷贝构造函数。但子类一旦提供拷贝构造和赋值运算符函数则不再调用父类拷贝构造和赋值运算符函数。

  1. /*继承中构造函数和析构函数的调用:构造函数和析构函数不可以 
  2. 被继承,但是可以被调用,而且子类肯定会调用父类的构造函数 
  3. 和析构函数。这种机制可以很自然的用于访问父类的私有成员*/  
  4. #include <iostream>  
  5. using namespace std;  
  6.   
  7. class A{  
  8. private:  
  9.     int x;  
  10. public:  
  11.     //A(){cout << "A()" << endl;}  
  12.     A(int x = 0):x(x){  
  13.         cout <<"A()构造"<<endl;  
  14.         cout << x << endl;}  
  15.     ~A(){cout << "~A()" << endl;}  
  16.     int _get(){  
  17.         return x;  
  18.     }  
  19. };  
  20.   
  21. class B:public A{  
  22. public:  
  23.     /*在初始化参数列表中可以指定调用父类的构造函数,指定调用构造函数并且给 
  24.     父类中的私有成员赋值*/  
  25.     /*注意:子类默认调用父类的无参构造,如果下面的代码没有:A(100),则会调用无参构造,但是父类无参构造 
  26.     被注释掉,所以会出错*/  
  27.     B():A(100){  
  28.         //x = 200;  
  29.         //A(100);  
  30.         cout << "B()" << endl;  
  31.     }  
  32.     //访问有参构造的方式,理解这种方式的作用  
  33.     /*注意,这种机制下的构造函数所赋的值是赋到了子类中的数据x中, 
  34.     而父类中的x仍然为0*/  
  35.     ~B(){cout << "~B()" << endl;}  
  36.     int getbx(){  
  37.         return _get();  
  38.     }  
  39. };  
  40. int main(){  
  41.     A a;//构建A对象,此时A类构造被调用,并打印出了值  
  42.   
  43.     B b;//B类为无参构造,首先调用了A的构造,在调用B的构造  
  44.   
  45.     //打印a对象中的x成员  
  46.     cout <<a._get()<<endl;//a对象中的x为0  
  47.   
  48.     //打印b对象中的x  
  49.     cout << b.getbx()<<endl;//是100  
  50.     /*一层一层的退,先调用b的析构,在调用a的析构*/  
  51. }  
/*继承中构造函数和析构函数的调用:构造函数和析构函数不可以
被继承,但是可以被调用,而且子类肯定会调用父类的构造函数
和析构函数。这种机制可以很自然的用于访问父类的私有成员*/
#include <iostream>
using namespace std;

class A{
private:
	int x;
public:
	//A(){cout << "A()" << endl;}
	A(int x = 0):x(x){
		cout <<"A()构造"<<endl;
		cout << x << endl;}
	~A(){cout << "~A()" << endl;}
	int _get(){
		return x;
	}
};

class B:public A{
public:
	/*在初始化参数列表中可以指定调用父类的构造函数,指定调用构造函数并且给
	父类中的私有成员赋值*/
	/*注意:子类默认调用父类的无参构造,如果下面的代码没有:A(100),则会调用无参构造,但是父类无参构造
	被注释掉,所以会出错*/
	B():A(100){
		//x = 200;
		//A(100);
		cout << "B()" << endl;
	}
	//访问有参构造的方式,理解这种方式的作用
	/*注意,这种机制下的构造函数所赋的值是赋到了子类中的数据x中,
	而父类中的x仍然为0*/
	~B(){cout << "~B()" << endl;}
	int getbx(){
		return _get();
	}
};
int main(){
	A a;//构建A对象,此时A类构造被调用,并打印出了值

	B b;//B类为无参构造,首先调用了A的构造,在调用B的构造

	//打印a对象中的x成员
	cout <<a._get()<<endl;//a对象中的x为0

	//打印b对象中的x
	cout << b.getbx()<<endl;//是100
	/*一层一层的退,先调用b的析构,在调用a的析构*/
}

拷贝构造和赋值运算符的问题

  1. #include <iostream>  
  2. using namespace std;  
  3. /*系统一旦提供构造函数,系统默认的构造函数将被回收 
  4. 记住,拷贝构造也是构造函数*/  
  5. class A{  
  6.     int arr;  
  7. public:  
  8.     A(){}  
  9.     //A(int x = 0):arr(x){}  
  10.     A(const A& a){  
  11.         cout << "父类拷贝构造" << endl;  
  12.     }  
  13.     void operator=(const A& a){  
  14.         cout << "父类赋值运算符函数" << endl;  
  15.     }  
  16. };  
  17. /*有指针类型的成员时,采用默认机制就麻烦了*/  
  18. class B:public A{  
  19.     //int * pi;  
  20. public:  
  21.     B(){}  
  22.     B(const B& b):A(b){                        
  23.         //子类中提供了拷贝构造函数将不再调用父类的拷贝构造  
  24.         cout << "子类拷贝构造" << endl;  
  25.     }  
  26.     void operator=(const B& b){  
  27.         A::operator=(b);         //调用父类的拷贝构造函数的机制  
  28.         cout << "子类赋值运算符函数"<< endl;  
  29.     }  
  30. };  
  31.   
  32. int main(){  
  33.     B a;  
  34.     B b = a;  
  35.     B c;  
  36.     c = a;  
  37. }  
#include <iostream>
using namespace std;
/*系统一旦提供构造函数,系统默认的构造函数将被回收
记住,拷贝构造也是构造函数*/
class A{
	int arr;
public:
	A(){}
	//A(int x = 0):arr(x){}
	A(const A& a){
		cout << "父类拷贝构造" << endl;
	}
	void operator=(const A& a){
		cout << "父类赋值运算符函数" << endl;
	}
};
/*有指针类型的成员时,采用默认机制就麻烦了*/
class B:public A{
	//int * pi;
public:
	B(){}
	B(const B& b):A(b){                      
		//子类中提供了拷贝构造函数将不再调用父类的拷贝构造
		cout << "子类拷贝构造" << endl;
	}
	void operator=(const B& b){
		A::operator=(b);         //调用父类的拷贝构造函数的机制
		cout << "子类赋值运算符函数"<< endl;
	}
};

int main(){
	B a;
	B b = a;
	B c;
	c = a;
}


8,名字隐藏

名字隐藏机制:子类中如果定义了和父类中同名的数据,这些数据包括成员变量和成员函数。则会把父类中的数据隐藏掉。

注意:只要名字相同,计算返回值或者形参列表不同,也会被隐藏。隐藏不代表就没有了,可以通过类名作用域::访问到被隐藏的成员。

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class A{  
  5. public:  
  6.     int x;  
  7.     int show(){  
  8.         cout << "show A" << endl;  
  9.         return 0;  
  10.     }  
  11.     A(){x=20;}  
  12.     A(int x):x(x){cout << "show A(int x)" << endl;}  
  13.     void shouu(){  
  14.         cout <<"shouu()"<<endl;  
  15.     }  
  16. };  
  17.   
  18. class B:public A{  
  19. public:  
  20.     int x;  
  21.     int y;  
  22.     B(){cout << "B()" << endl;}  
  23.     void show(){  
  24.         cout << "show B" << endl;  
  25.         //A::show();  
  26.     }  
  27. };  
  28.   
  29. int main(){  
  30.     B b;  
  31.     b.shouu();  
  32.     //cout << b.x << endl;  
  33.     //cout << b.A::x << endl;   //突破名字隐藏机制  
  34.     //int c = b.show();被隐藏,无法访问  
  35.     //b.A::show();  
  36. }  
#include <iostream>
using namespace std;

class A{
public:
	int x;
	int show(){
		cout << "show A" << endl;
		return 0;
	}
	A(){x=20;}
	A(int x):x(x){cout << "show A(int x)" << endl;}
	void shouu(){
		cout <<"shouu()"<<endl;
	}
};

class B:public A{
public:
	int x;
	int y;
	B(){cout << "B()" << endl;}
	void show(){
		cout << "show B" << endl;
		//A::show();
	}
};

int main(){
	B b;
	b.shouu();
	//cout << b.x << endl;
	//cout << b.A::x << endl;   //突破名字隐藏机制
	//int c = b.show();被隐藏,无法访问
	//b.A::show();
}


9、多继承和函数重写

多继承是c++特有的语法机制,表现为一个子类有多个直接的父类。

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class phone{  
  5.     double price;  
  6. public:  
  7.     //phone();  
  8.     phone(double price = 15):price(price){cout << "phone" << endl;}  
  9.     ~phone(){cout << "~phone" << endl;}  
  10.     void call(){  
  11.         cout << "use calling" << endl;  
  12.     }  
  13.     double getprice(){  
  14.         return price;  
  15.     }  
  16. };  
  17.   
  18. class MP3{  
  19.     double price;  
  20. public:  
  21.     MP3(double price = 20):price(price){cout << "MP3" << endl;}  
  22.     ~MP3(){cout << "~MP3" << endl;}  
  23.     void play(){  
  24.         cout << "use to listening music" << endl;  
  25.     }  
  26.     double getprice(){  
  27.         return price;  
  28.     }  
  29. };   
  30.   
  31. class vedio{  
  32.     double price;  
  33. public:  
  34.     vedio(double price = 0):price(price){cout << "vedio" << endl;}  
  35.     ~vedio(){cout << "~vedio" << endl;}  
  36.     void vcd(){  
  37.         cout << "watch vedio" << endl;  
  38.     }   
  39.     double getprice(){  
  40.         return price;  
  41.     }  
  42. };  
  43. /*多继承*/  
  44. class iphone:public phone,public MP3,public vedio{  
  45. public:  
  46.     double getprice(){  
  47.         return phone::getprice() + MP3::getprice() + vedio::getprice();  
  48.     }  
  49. };  
  50.   
  51. int main(){  
  52.     iphone iphone6;  
  53.     //cout << sizeof(iphone) << endl;  
  54.     cout << iphone6.MP3::getprice() << endl;  
  55.     cout << iphone6.phone::getprice() << endl;  
  56.     cout << iphone6.getprice() << endl;    //用名字隐藏机制解决多分数据同名冲突的问题  
  57. }  
#include <iostream>
using namespace std;

class phone{
	double price;
public:
	//phone();
	phone(double price = 15):price(price){cout << "phone" << endl;}
	~phone(){cout << "~phone" << endl;}
	void call(){
		cout << "use calling" << endl;
	}
	double getprice(){
		return price;
	}
};

class MP3{
	double price;
public:
	MP3(double price = 20):price(price){cout << "MP3" << endl;}
	~MP3(){cout << "~MP3" << endl;}
	void play(){
		cout << "use to listening music" << endl;
	}
	double getprice(){
		return price;
	}
}; 

class vedio{
	double price;
public:
	vedio(double price = 0):price(price){cout << "vedio" << endl;}
	~vedio(){cout << "~vedio" << endl;}
	void vcd(){
		cout << "watch vedio" << endl;
	} 
	double getprice(){
		return price;
	}
};
/*多继承*/
class iphone:public phone,public MP3,public vedio{
public:
	double getprice(){
		return phone::getprice() + MP3::getprice() + vedio::getprice();
	}
};

int main(){
	iphone iphone6;
	//cout << sizeof(iphone) << endl;
	cout << iphone6.MP3::getprice() << endl;
	cout << iphone6.phone::getprice() << endl;
	cout << iphone6.getprice() << endl;    //用名字隐藏机制解决多分数据同名冲突的问题
}


多继承遇到的问题:上面的代码用sizeof就可以看到,子类在多继承的时候会多次复制顶层数据,而我们期望的是price这个成员只需要复制一份就可以了,因为多余的复制是无意义的。首先采用顶层抽象的方式,将三个父类抽象到更高的层面上。

  1. #include <iostream>  
  2. using namespace std;  
  3. /*抽象到更高层的类中*/  
  4. class product{  
  5.     double price;  
  6. public:  
  7.     double getprice(){  
  8.         return price;  
  9.     }  
  10.     product(double price = 0):price(price){cout <<"product"<<endl;}  
  11. };  
  12. class phone:public product{  
  13. public:  
  14.     //phone();  
  15.     phone(double price = 15):product(price){cout << "phone" << endl;}  
  16.     ~phone(){cout << "~phone" << endl;}  
  17.     void call(){  
  18.         cout << "use calling" << endl;  
  19.     }  
  20. };  
  21.   
  22. class MP3:public product{  
  23. public:  
  24.     MP3(double price = 20):product(price){cout << "MP3" << endl;}  
  25.     ~MP3(){cout << "~MP3" << endl;}  
  26.     void play(){  
  27.         cout << "use to listening music" << endl;  
  28.     }  
  29. };   
  30.   
  31. class vedio:public product{  
  32. public:  
  33.     vedio(double price = 0):product(price){cout << "vedio" << endl;}  
  34.     ~vedio(){cout << "~vedio" << endl;}  
  35.     void vcd(){  
  36.         cout << "watch vedio" << endl;  
  37.     }   
  38. };  
  39. class iphone:public phone,public MP3,public vedio{  
  40.       
  41. };  
  42.   
  43. int main(){  
  44.     iphone iphone6;  
  45.     //cout << iphone6.getprice() << endl;同样会产生冲突的问题  
  46.     //cout << sizeof(iphone) << endl;  
  47.     cout << iphone6.MP3::getprice() << endl;  
  48.     cout << iphone6.phone::getprice() << endl;  
  49.     //cout << iphone6.getprice() << endl;//直接调用产生冲突问题,编译器不知道该调用哪一个  
  50. }  
#include <iostream>
using namespace std;
/*抽象到更高层的类中*/
class product{
	double price;
public:
	double getprice(){
		return price;
	}
	product(double price = 0):price(price){cout <<"product"<<endl;}
};
class phone:public product{
public:
	//phone();
	phone(double price = 15):product(price){cout << "phone" << endl;}
	~phone(){cout << "~phone" << endl;}
	void call(){
		cout << "use calling" << endl;
	}
};

class MP3:public product{
public:
	MP3(double price = 20):product(price){cout << "MP3" << endl;}
	~MP3(){cout << "~MP3" << endl;}
	void play(){
		cout << "use to listening music" << endl;
	}
}; 

class vedio:public product{
public:
	vedio(double price = 0):product(price){cout << "vedio" << endl;}
	~vedio(){cout << "~vedio" << endl;}
	void vcd(){
		cout << "watch vedio" << endl;
	} 
};
class iphone:public phone,public MP3,public vedio{
	
};

int main(){
	iphone iphone6;
	//cout << iphone6.getprice() << endl;同样会产生冲突的问题
	//cout << sizeof(iphone) << endl;
	cout << iphone6.MP3::getprice() << endl;
	cout << iphone6.phone::getprice() << endl;
	//cout << iphone6.getprice() << endl;//直接调用产生冲突问题,编译器不知道该调用哪一个
}

上面的代码中,product的构造函数 被调用了三次,因为这种继承是一级一级的来的,构造子类的时候找父类,发现父类还有父类,就去调用爷爷类的构造函数,三次继承,三次调用。


这种继承方式构成了一种菱形或者钻石型的继承,叫做菱形继承或者钻石继承,但钻石继承并没有实际解决数据多次复制的问题,为了解决菱形继承,c++提出了虚继承。虚继承就是在继承的时候加上virtual关键字修饰即可。虚继承对于共同的成员父亲类从爷爷类那里继承来的,这里为double price,子类直接越级访问,直接从爷爷类那里继承price。

  1. /*类中也会有对齐和补齐*/  
  2. #include <iostream>  
  3. using namespace std;  
  4. /*抽象到更高层的类中*/  
  5. class product{  
  6.     int price;  
  7. public:  
  8.     int  getprice(){  
  9.         return price;  
  10.     }  
  11.     product(double price = 0):price(price){cout << "product" << endl;}  
  12. };  
  13.   
  14. class phone:virtual public product{  
  15. public:  
  16.     //phone();  
  17.     phone(double price = 15):product(price){cout << "phone" << endl;}  
  18.     ~phone(){cout << "~phone" << endl;}  
  19.     void call(){  
  20.         cout << "use calling" << endl;  
  21.     }  
  22. };  
  23.   
  24. class MP3:virtual public product{  
  25. public:  
  26.     MP3(double price = 20):product(price){cout << "MP3" << endl;}  
  27.     ~MP3(){cout << "~MP3" << endl;}  
  28.     void play(){  
  29.         cout << "use to listening music" << endl;  
  30.     }  
  31. };   
  32.   
  33. class vedio:virtual public product{  
  34. public:  
  35.     vedio(double price = 0):product(price){cout << "vedio" << endl;}  
  36.     ~vedio(){cout << "~vedio" << endl;}  
  37.     void vcd(){  
  38.         cout << "watch vedio" << endl;  
  39.     }   
  40. };  
  41. class iphone:virtual public phone,virtual public MP3,virtual public vedio{  
  42. public:  
  43.     iphone(int m = 0,int v = 0,int p = 0):product(m + p + v){}  
  44. };  
  45. /*虚函数之后,product的构造函数只被调用了一次,孙子类直接越级访问 
  46. 了product类*/  
  47. int main(){  
  48.     iphone iphone6(1000,2041,3201);  
  49.     //cout << iphone6.getprice() << endl;同样会产生冲突的问题  
  50.     //cout << sizeof(iphone) << endl;  
  51.     //cout << iphone6.MP3::getprice() << endl;  
  52.     //cout << iphone6.phone::getprice() << endl;  
  53.     //cout << iphone6.getprice() << endl;直接调用产生冲突问题,编译器不知道该调用哪一个  
  54.     cout << sizeof(iphone) << endl;  
  55.     cout << iphone6.getprice() << endl;  
  56. }  
/*类中也会有对齐和补齐*/
#include <iostream>
using namespace std;
/*抽象到更高层的类中*/
class product{
	int price;
public:
	int  getprice(){
		return price;
	}
	product(double price = 0):price(price){cout << "product" << endl;}
};

class phone:virtual public product{
public:
	//phone();
	phone(double price = 15):product(price){cout << "phone" << endl;}
	~phone(){cout << "~phone" << endl;}
	void call(){
		cout << "use calling" << endl;
	}
};

class MP3:virtual public product{
public:
	MP3(double price = 20):product(price){cout << "MP3" << endl;}
	~MP3(){cout << "~MP3" << endl;}
	void play(){
		cout << "use to listening music" << endl;
	}
}; 

class vedio:virtual public product{
public:
	vedio(double price = 0):product(price){cout << "vedio" << endl;}
	~vedio(){cout << "~vedio" << endl;}
	void vcd(){
		cout << "watch vedio" << endl;
	} 
};
class iphone:virtual public phone,virtual public MP3,virtual public vedio{
public:
	iphone(int m = 0,int v = 0,int p = 0):product(m + p + v){}
};
/*虚函数之后,product的构造函数只被调用了一次,孙子类直接越级访问
了product类*/
int main(){
	iphone iphone6(1000,2041,3201);
	//cout << iphone6.getprice() << endl;同样会产生冲突的问题
	//cout << sizeof(iphone) << endl;
	//cout << iphone6.MP3::getprice() << endl;
	//cout << iphone6.phone::getprice() << endl;
	//cout << iphone6.getprice() << endl;直接调用产生冲突问题,编译器不知道该调用哪一个
	cout << sizeof(iphone) << endl;
	cout << iphone6.getprice() << endl;
}


这个代码中product的构造函数只调用了一次,说明子类直接越级访问了爷爷类的数据。而对于父类特有的子类照常继承,只是没有通过父类去继承爷爷类的数据成员,所以product的构造函数只被调用了一次。


虚函数:在函数前面加上virtual关键字修饰过的就是虚函数.

#include <iostream>
using namespace std;
class A{
int x;
public:
virtual void show(){}
virtual void showa(){}
};
int main(){
cout << sizeof(A) << endl;
}

虚函数的主要表现为会占用四个字节的空间,只要成员中出现虚函数,不管有多少个虚函数,都只用四个字节来维护这个虚关系。虚函数会影响对象的大小。维护虚关系使用一个指针来维护的,所以是四个字节。


函数重写:

在父类中出现一个虚函数,如果在子类中提供和父类同名的函数(注意区分名字隐藏),这就加函数重写。

函数重写要求必须有相同函数名,相同的参数列表,相同的返回值。


三、c++面向对象之多态

1、多态:一个父类型的对象的指针或者引用指向或者是引用一个子类对象时,调用父类型中的虚函数,如果子类覆盖了虚函数,则调用的表现是子类覆盖之后的。

继承是构成多态的基础;

虚函数是构成多态的关键;

函数覆盖是构成多态的必备条件;

多态的应用:函数参数,函数返回值。

多态的产生必须依靠上面的四点,缺一不可。


2、多态的应用,多态相对做到了通用类型编程,主要用在函数参数和函数的返回值上。

  1. /*多态的应用: 
  2. 1、函数参数 
  3. 2、函数返回值*/  
  4. #include <iostream>  
  5. using namespace std;  
  6. class Animal{  
  7. public:  
  8.     virtual void run(){  
  9.         cout <<"Ainimal run()"<<endl;  
  10.     }  
  11.     void show(){  
  12.         cout <<"Animal show()"<<endl;  
  13.     }  
  14. };  
  15.   
  16. class Dog:public Animal{  
  17. public:  
  18.     void run(){  
  19.         cout <<"Dog run()"<<endl;  
  20.     }  
  21.     void show(){  
  22.         cout <<"dog show()"<<endl;  
  23.     }  
  24. };  
  25.   
  26. class Cat:public Animal{  
  27. public:  
  28.     void run(){  
  29.         cout <<"cat run()"<<endl;  
  30.     }  
  31. };  
  32. /*多态用作函数参数*/  
  33. void showAnimal(Animal * animal){  
  34.     animal->show();  
  35.     animal->run();  
  36. }  
  37. /*多态用作返回值*/  
  38. Animal * getAnimal(int x){  
  39.     if (1 == x)  
  40.         return new Dog();  
  41.     if (2 == x)  
  42.         return new Cat();  
  43. }  
  44. int main(){  
  45.     Cat cat;  
  46.     showAnimal(&cat);  
  47.     Dog dog;  
  48.     showAnimal(&dog);  
  49. }  
/*多态的应用:
1、函数参数
2、函数返回值*/
#include <iostream>
using namespace std;
class Animal{
public:
	virtual void run(){
		cout <<"Ainimal run()"<<endl;
	}
	void show(){
		cout <<"Animal show()"<<endl;
	}
};

class Dog:public Animal{
public:
	void run(){
		cout <<"Dog run()"<<endl;
	}
	void show(){
		cout <<"dog show()"<<endl;
	}
};

class Cat:public Animal{
public:
	void run(){
		cout <<"cat run()"<<endl;
	}
};
/*多态用作函数参数*/
void showAnimal(Animal * animal){
	animal->show();
	animal->run();
}
/*多态用作返回值*/
Animal * getAnimal(int x){
	if (1 == x)
		return new Dog();
	if (2 == x)
		return new Cat();
}
int main(){
	Cat cat;
	showAnimal(&cat);
	Dog dog;
	showAnimal(&dog);
}


3、多态的实现原理

多态的实现主要依赖于下面的三个东西:

虚函数:成员函数加了virtual修饰

虚函数表指针:一个类型有虚函数,则对这个类型提供一个指针,这个指针放在生成对象的前四个字节。同类型的对象共享一张虚函数表。并且不同类型的虚函数表地址不同。

虚函数表:虚函数表中的每个元素都是虚函数的地址。

一个类型一旦出现虚函数,则会生成一张虚函数表,虚函数表中存放的就是虚函数的函数地址,通过这个函数地址可以到代码区中去执行对应的函数。虚函数表中只存放类型中的虚函数,不是虚函数的一概不管。在每个生成的类型对象的前四个字节中存放的是虚函数表指针,通过这个指针可以访问到虚函数表,从而访问其中的函数。同种类型共享虚函数表,不同类型有自己独立的虚函数表,继承关系中的子类和父类属于不同类型,所以有自己独立的函数表。


  1. /*多态的原理*/  
  2. #include <iostream>  
  3. #include <cstring>  
  4. using namespace std;  
  5. class Animal{  
  6.     int x;  
  7. public:  
  8.     virtual void fun(){  
  9.         cout<< "Aniaml fun()"<<endl;  
  10.     }  
  11.     virtual void run(){  
  12.         cout <<"Animal run()"<<endl;  
  13.     }  
  14.     void show(){  
  15.         cout <<"Animal show()"<<endl;  
  16.     }  
  17. };  
  18.   
  19. class Dog:public Animal{  
  20. public:  
  21.     virtual void fun(){  
  22.         cout << "dog run"<<endl;  
  23.     }  
  24.     void run(){  
  25.         cout <<"dog run"<<endl;  
  26.     }  
  27. };  
  28.   
  29. class Cat:public Animal{  
  30. public:  
  31.     void fun(){  
  32.         cout <<"cat fun"<<endl;  
  33.     }  
  34. };  
  35.   
  36. int main(){  
  37.     Animal a;  
  38.     Animal b;  
  39.     /*取出虚函数表的地址并打印*/  
  40.     int * pi = (int*)&a;  
  41.     cout <<showbase<< hex << *pi<<endl;  
  42.   
  43.     pi = reinterpret_cast<int*>(&b);  
  44.     cout <<showbase<< hex << *pi<<endl;  
  45.   
  46.     /*子类不会和父类共享虚函数表,地址不一样*/  
  47.     Dog dog;  
  48.     pi = reinterpret_cast<int*>(&dog);  
  49.     cout <<showbase<< hex << *pi<<endl;  
  50.   
  51.     Animal * pcat = new Cat();  
  52.     pcat->run();  
  53.     pcat->fun();  
  54.   
  55.     /*更改dog的虚表的值,我们把dog的虚表地址 
  56.     改成cat的虚表地址*/  
  57.     Animal * pdog = new Dog();  
  58.     pdog->run();  
  59.     pdog->fun();  
  60.   
  61.     /*更换dog的虚表地址,将cat的前四个字节 
  62.     移动到dog的前四个字节*/  
  63.     memcpy(pdog,pcat,4);  
  64.     pdog->run();  
  65.     pdog->fun();  
  66.     /*上面的更改后,狗变成了猫的特性*/  
  67. }  
/*多态的原理*/
#include <iostream>
#include <cstring>
using namespace std;
class Animal{
	int x;
public:
	virtual void fun(){
		cout<< "Aniaml fun()"<<endl;
	}
	virtual void run(){
		cout <<"Animal run()"<<endl;
	}
	void show(){
		cout <<"Animal show()"<<endl;
	}
};

class Dog:public Animal{
public:
	virtual void fun(){
		cout << "dog run"<<endl;
	}
	void run(){
		cout <<"dog run"<<endl;
	}
};

class Cat:public Animal{
public:
	void fun(){
		cout <<"cat fun"<<endl;
	}
};

int main(){
	Animal a;
	Animal b;
	/*取出虚函数表的地址并打印*/
	int * pi = (int*)&a;
	cout <<showbase<< hex << *pi<<endl;

	pi = reinterpret_cast<int*>(&b);
	cout <<showbase<< hex << *pi<<endl;

	/*子类不会和父类共享虚函数表,地址不一样*/
	Dog dog;
	pi = reinterpret_cast<int*>(&dog);
	cout <<showbase<< hex << *pi<<endl;

	Animal * pcat = new Cat();
	pcat->run();
	pcat->fun();

	/*更改dog的虚表的值,我们把dog的虚表地址
	改成cat的虚表地址*/
	Animal * pdog = new Dog();
	pdog->run();
	pdog->fun();

	/*更换dog的虚表地址,将cat的前四个字节
	移动到dog的前四个字节*/
	memcpy(pdog,pcat,4);
	pdog->run();
	pdog->fun();
	/*上面的更改后,狗变成了猫的特性*/
}


上述程序对应的内存图:


可以看出,一旦满足了多态的条件,程序自然按照上图的流程执行。

4、上面既然说了,虚函数表中存放的是函数的地址,那么能不能直接我们自己取出虚函数的地址,直接调用所需要的函数呢?

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Animal{  
  5. public:  
  6.     virtual void run(int x){  
  7.         cout <<"run x="<<x<<endl;  
  8.     }  
  9.     virtual void fun(int x){  
  10.         cout <<"fun x="<<x<<endl;  
  11.     }  
  12.     void show(){  
  13.         cout <<"this is show()"<<endl;  
  14.     }  
  15. };  
  16.   
  17.   
  18. int main()  
  19. {  
  20.     /*去掉函数名就是函数指针的类型,指针简化操作*/  
  21.     typedef void (*MFUN)(Animal* a,int x);/*MFUN就是虚表中的函数指针类型*/  
  22.   
  23.     typedef MFUN* VTABLE;//MFUN*就是虚表类型  
  24.   
  25.     Animal animal;  
  26.     VTABLE vt = *((VTABLE*)&animal);  
  27.     /*虚函数表表现为函数指针数组*/  
  28.     vt[0](&animal,100);  
  29.     vt[1](&animal,123);  
  30.     return 0;  
  31. }  
#include <iostream>
using namespace std;

class Animal{
public:
	virtual void run(int x){
		cout <<"run x="<<x<<endl;
	}
	virtual void fun(int x){
		cout <<"fun x="<<x<<endl;
	}
	void show(){
		cout <<"this is show()"<<endl;
	}
};


int main()
{
	/*去掉函数名就是函数指针的类型,指针简化操作*/
	typedef void (*MFUN)(Animal* a,int x);/*MFUN就是虚表中的函数指针类型*/

	typedef MFUN* VTABLE;//MFUN*就是虚表类型

	Animal animal;
	VTABLE vt = *((VTABLE*)&animal);
	/*虚函数表表现为函数指针数组*/
	vt[0](&animal,100);
	vt[1](&animal,123);
	return 0;
}


虚表中存放的就是虚函数的函数指针,可以理解为函数指针的数组,通过typedef将指针降级。


5,虚析构函数

virtual关键字只能修饰成员函数或者析构函数,其他的函数都不行。

当我们用new创建一个指向子类对象的父类指针时,例如Animal * animal = new Dog()时,其中Animal时父类,Dog是子类,并且delete animal时,其子类对象的析构函数不会被调用,只会调用父类的析构函数。所以就会遇到一个问题,如果子类对象有自己独立的堆内存时,这部分内存就无法释放。这时,我们只需要在父类的析构函数上用virtual修饰即可,子类析构函数就会被调用。

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Animal{  
  5. public:  
  6.     Animal(){  
  7.         cout <<"Animal()"<<endl;  
  8.     }  
  9.     virtual ~Animal(){  
  10.         cout <<"~Animal()"<<endl;  
  11.     }  
  12. };  
  13. class Dog:public Animal{  
  14. public:  
  15.     Dog(){  
  16.         cout <<"Dog()"<<endl;  
  17.     }  
  18.     ~Dog(){  
  19.         cout <<"~Dog()"<<endl;  
  20.     }  
  21. };  
  22. int main(){  
  23.     Animal * pa = new Dog();  
  24.     delete pa;  
  25.     /*子类析构函数的调用必然引发父类析构*/  
  26. }  
#include <iostream>
using namespace std;

class Animal{
public:
	Animal(){
		cout <<"Animal()"<<endl;
	}
	virtual ~Animal(){
		cout <<"~Animal()"<<endl;
	}
};
class Dog:public Animal{
public:
	Dog(){
		cout <<"Dog()"<<endl;
	}
	~Dog(){
		cout <<"~Dog()"<<endl;
	}
};
int main(){
	Animal * pa = new Dog();
	delete pa;
	/*子类析构函数的调用必然引发父类析构*/
}


虚析构函数的应用场景:

当父类中有虚函数时,应该吧父类的析构函数定义成虚析构函数。

子类和父类中都有自己的堆内存分配时。

【C++核心】面向对象的三大特性 静态多态: 函数重载和运算符重载属于静态多态,复用函数名。动态多态: 派生类和虚函数实现运行时多态。静态多态的函数地址早绑定:编译阶段确定函数地址。动态多态的函数地址晚绑定:运行阶段确定函数地址。动态多态的本质是父类指针或引用指向子类对象。本部分所介绍的多态,指的都是动态多态。因此,为了方便起见,下文只要说到多态的地方,都代表动态多态。 阅读详情

相关推荐

cadence allegro 17.x 降版本到16.6的转换工具

Cadence17.x版本降低到16.6版本转换工具 下载链接:Cadence17.x版本降低到16.6版本转换工具   使用方法: 软件使用比较简单,在「第一个路径输入栏」浏览指向17.x的设计文件,也就是需要降版本的17.4或者17.2的文件,然后在「第二个路径输入栏」里浏览指向转换后的文件保存路径,然后点击「转换」按钮即可。 ...

ltqdxl的博客 1万+

C++—面向对象三大特性以及多态重点详解

面向对象三大特性、C++多态详解

拥抱技术 1650

b站up“麦辣翅翅”《YOLOv8修改损失函数》问题解决RuntimeError: The size of tensor a (4) must match the size of tensor

b站up“麦辣翅翅”《YOLOv8修改损失函数》问题解决RuntimeError: The size of tensor a (4) must match the size of tensor ;TypeError: bbox_iou() got an unexpected keyword argument 'xywh‘

weixin_68331871的博客 3020

C++ 面向对象三大特征总结(详解)

1.面向对象的三大特征 1).封装 封装:将一个对象的全部的属性变量和行为方法进行包装(集中到一个类中,), 并用权限对其成员属性和成员方法加以限制,使得外部对其访问时,不能随意改变该包装. #include <iostream> using namespace std; class Shape{ private: int x;//重心的横坐标 int y;//重心的纵坐标 public : Shape(int x = 0,int y =0):x(x),y(

qq_52575999的博客 1701

电网渗透之终端安全

        电力系统安全不仅关系到电网的稳定运行,也关系到国家能源安全和国计民生,甚至关系到国家的安全和利益,电力系统由于结构复杂多样、分布广泛、重要性高,一旦系统瘫痪影响巨大,因此,极容易成为敌对势力攻击的首选目标。随着信息安全暴露面迅速增加,将给公司网络安全防线带来严峻挑战。       监控、保障这些电力系统安全运行变的尤为重要。内部安全防护产品有防火墙、纵向加密、反向隔离设备等...

guapi007008的博客 2721

双击文件夹弹窗“找不到应用程序”,但是右键点击打开可以

此电脑、回收站、c、d、f盘、控制面板、任意文件夹等都没有办法双击打开。 右键中包含“使用filemanager打开”,是由于FileManager卸载残留导致的。 懒人专用附件 下载解压上面附件,双击导入可以清理FileManager软件残留 参考: 双击此电脑出现找不到应用程序,但是右键点击可以-其他-优快云问答https://ask.youkuaiyun.com/questions/7631439 电脑上的文件夹双击都提示找不到应用程序,但是右击打开就能打开,该怎么解决? - 知乎 (z

webmasterqpwou的博客 1万+

C++之三大特性 “封装”、“继承”、“多态”

C++之三大特性 “封装”、“继承”、“多态” 目录封装继承继承的概念及定义基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员复杂的菱形继承及菱形虚拟继承继承的总结和反思多态二级目录三级目录 封装 继承 继承的概念及定义 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触

qq_49613557的博客 1万+

Signoff Criteria --- ocv/aocv/pocv之AOCV介绍

Advanced on chip variation,相比较于OCV来说,对于悲观度的去除很有效。相比较与ocv对于整个design中的lauch/capture path设置统一的derate值,aocv通过将cell derate值与cell的逻辑深度以及实际的位置结合起来进行derate值的设定。那么这种derate如何能够设置到具体的cell呢,其实是我们在跑STA时候会读入相关的aocv lib file,里面有专门对相关的cell进行的aocv derate值的描述。> table: \

sinat_27691203的博客 7853

17. 面向对象的特征

继承,其基本思想就是基于某个父类的扩展,制定出一个新的子类,子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。通过继承可以直接让子类获取父类的方法和属性,避免编写重复性的代码,并且也符合 OCP 原则。所以,我们经常需要通过继承来对一个类进行扩展。继承的出现让类与类之间产生了is-a的关系。class 子类名 : 权限修饰符 父类名 {// 类的成员 }一旦子类继承父类之后,子类就获取父类中声明的所有的结构:成员变量和成员函数;

flurry_heart的博客 872

YOLOv11改进 | 网络结构代码逐行解析(二) | yolov11中的Mosaic增强详解

本文给大家带来的是YOLOv10中的Mosaic增强代码的详解,可能有部分人对于这一部分比较陌生,有的读者可能知道Mosaic增强但是不知道其工作原理,具体来说Mosaic增强就是指我们的数据集中的图片在输入给模型之前的一个处理过程(我们的图片并不是直接就输入给模型了,大家的训练结果中的结果检测图片大家可以看到数据集中多个图片会组合在一起这就是简单的Mosaic增强),下面我就来讲解一下其在YOLOv10中工作原理和代码定义,下面图片为一个Mosaic增强后的图片。​。

Snu77的博客 5018

【OpenClaw从入门到精通】第13篇:OpenClaw低代码编排实战:非技术人员也能玩转AI自动化流程(2026新版实测)

本文聚焦OpenClaw 2026版核心升级的低代码编排能力,针对非技术人员的实操需求,从底层逻辑、双环境部署、三类实战案例、避坑指南四个维度展开。详解阿里云集群版(企业级)与汉化本地版(个人调试)的部署步骤,通过基础数据统计、公众号内容生产线、企业级任务调度三个递进式案例,演示可视化拖拽搭建自动化流程的完整过程。全程零代码/低代码操作,配套详细参数配置、执行结果模拟及问题排查方案,帮助销售、运营、自媒体等非技术人员快速掌握自动化工具,将重复工作交给AI,提升核心工作效率。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 615

设计模式(1)——面向对象和面向过程,封装、继承和多态

要学习设计模式,首先需要了解什么是面向对象,并掌握其三大要素:封装、继承和多态。我们可以通过一个简单的例子来说明:假设我们想要把一头大象放进冰箱,这个过程可以分为三个步骤:1)打开冰箱门;2)把大象放进去;3)关上冰箱门。在面向过程的编程中,这三个步骤通常被抽象为三个函数,并在调用时按需提供参数。而在面向对象的编程中,需要围绕具体的对象进行设计。这里有两个关键对象:冰箱和大象。冰箱需要具备开门和关门的功能;大象则需要具备进入冰箱和离开冰箱的功能。对象是类的实例。

m0_63086198的博客 1543

[QMT量化交易小白入门]-104、QMT平台运行深度学习模型,预测期货价格

本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步。

木头大左的博客 479

C++核心高级编程 --- 4.类和对象

当其他类对象作为本类成员时,构造时先构造类对象,再构造自身,析构的顺序与构造相反。

zhongziqia的博客 1178

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

C++面向对象基础

用new关键字创建,对象类型的指针(类*) ,指向new关键字开辟的空间。通常是把类中的属性变为私有(private),如果外部访问需要公共接口,可以控制属性的读和写的权限,提高了程序的安全性。2、如果没有显式给出构造函数,编译器会给出默认的构造函数(参数为空,并且函数体也为空),并没有实际操作,如果给出任意的构造函数,系统默认的构造函数就不存在了。2、如果不显式地写出拷贝构造函数,编译器会给出默认的拷贝构造函数,完成对象之间的值复制,如果显式地写出拷贝构造函数,编译器就不会提供默认的拷贝构造函数。

2201_75443889的博客 1314

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

类和对象(上)

C语言是面向过程的语言,而C++是面向对象的语言。

klausur31的博客 784

C++面向对象

1.面向对象的三大特征是哪些?各自右什么样的特点?` 1.封装:将客观实物封装成抽象的类,而类可以把自己的数据和方法暴露给可信的类或对象,对不可信的类活对象则进行信息隐藏; 2.继承:可以利用现有类的所有功能,并且无需重新编写原来的类即可对类的功能进行拓展; 3.多态:一个类实例的相同方法在不同的情形下有不同的表现形式,使不同内部结构的对象可以共享相同的数据; ...........................

weixin_40492248的博客 560

C++之类(持续更新)

(1)声明和定义都放在类体中// .h文件中public:// 共有成员(外部接口,可被使用该类的所有代码所使用的)student();~student();private:// 私有成员(只允许本类中的函数访问,而类外部的任何函数都不允许访问)int age;protected:// 保护成员(与private类似,差别表现在继承与派生时)(2)声明和定义分离// .h文件中public:// 共有成员(外部接口,可被使用该类的所有代码所使用的)student();

专注 1272

江苏c语言编程题格式,江苏二级c语言基础知识题.doc

江苏二级c语言基础知识题第6章 C++语言概述?6.1? 面向对象的程序设计??? 6.1.1? 考点1:面向对象程序设计的三个特征??? C++是一种面向对象的程序设计语言,充分支持面向对象方法中的三个主要特征。??? 1.封装性??? 封装是一种信息隐蔽技术,是指将数据和算法捆绑成一个整体,存取数据时只需知道其算法的外部接口而无须了解数据的内部结构。C++语言通过建立“类”来支持封装性和信息隐...

weixin_39922534的博客 121

C++ 类和对象 友元 内部类 this指针 默认成员函数 初始化列表……

在构造函数中初始化成员变量(效率更高,必须用于const成员)。通过虚函数实现运行时绑定(需继承和虚函数支持)。指向当前对象的指针,用于区分成员变量与局部变量。将数据和操作数据的方法绑定,隐藏实现细节。定义在类内部的类,可访问外部类的静态成员。允许新类继承现有类的成员,实现代码复用。没有名字的临时对象,生命周期仅限当前行。允许自定义运算符对类对象的操作。

azaz_plus的博客 621

C++基础知识

C++基础 输入输出 出入的是流对象,头文件需要iostream int a,b,c; cin>>a>>b>>c; >cout<<"a+b="<<c; using namespace std; 只要用到类或者对象都要写 类 包括了:类名、私有部分(对外不可以访问)、公有部分、 每个部分都有对应的变量和函数;其中,私有变量和函数只能够在私有部分的使用。 一般来说类的属性成员都应设置为private, public只留给那些被外界用来调用的

Freyua_xx的博客 286

c/c++面试题(5)(c++重要的概念详解)

1.C++面向对象的三大特征? 1)封装:将客观事物封装成抽象的类,并且设计者可以对类的成员进行访问控制权限控制. 这样一方面可以做到数据的隐藏,保护数据安全;另一方面,封装可以修改类的内部 实现而不用修改调用了该类的用户的代码.同时封装还有利于代码的方便复用; 2)继承:a.继承具有这样一种功能,它可以使用现有类的所有功能;并且可以在不重...

weixin_33806300的博客 162

【c++ 封装、继承、多态】

函数运行的版本不是一开始就确定好了的而是在程序运行的时候才选择函数运行的版本,至于到底选择哪个函数版本取决于调用该函数的指针或者引用实际绑定的数据对象。

小呆鸟blog 6763

C++封装、继承、多态

C++封装继承多态总结 面向对象的三个基本特征 面向对象的三个基本特征是:封装、继承、多态。其中,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用! 封装

ruyue_ruyue的专栏 10万+

【C++面向对象】六、封装、继承和多态

生活中的多态,是指的客观的事物在人脑中的主观体现。例如,在路上看到一只哈士奇,你可以看做是哈士奇,可以看做是狗,也可以看做是动物。主观意识上的类别,与客观存在的事物,存在is a的关系的时候,即形成了多态。在程序中,一个类的引用指向另外一个类的对象,从而产生多种形态。当二者存在直接或者间接的继承关系时,父类引用指向子类的对象,即形成多态。多态是面向对象三大特性之一,记住继承是多态的前提,如果类与类之间没有继承关系,也不会存在多态。

咖喱年糕的博客 1388
上一篇: 谈谈8年C++面向对象设计的经验体会
下一篇: C++继承与派生
csshuke
博客等级 码龄16年 1517粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值