1.构造函数一般声明为公有成员,但它不需要也不能像其他成员函数那样被显示地调用,它是在定义对象的同时被自动调用,而且只执行一次。
2.C++中某些类型的成员是不允许在构造函数中用赋值语句直接赋值的。例如:对于用const修饰的数据成员,或是引用类型的数据成员,是不允许用赋值语句直接赋值的。因此,只能用成员初始化列表对其进行初始化。
注:引用只在声明时进行初始化。不能声明后再赋值。
注:类成员是按照他们在类里被声明的顺序进行初始化的,与它们在成员初始化列表中列出的顺序无关。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class A{
public:
A(int x1):x(x1),rx(x),pi(3.14){
}
void print(){
cout<<"x="<<x<<endl;
cout<<"rx="<<rx<<endl;
cout<<"pi="<<pi<<endl;
}
private:
int x;
int ℞
const double pi;
};
int main()
{
A a(10);
a.print() ;
return 0;
}
3.一个类只能有一个析构函数。
4.拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。拷贝构造函数的作用是在建立一个新对象时,使用一个已经存在的对象去初始化这个新对象。
注:拷贝构造函数只有一个参数,并且是同类对象的引用。
每个类必须有一个拷贝构造函数。
5.静态数据成员。
注:定义类时,定义普通的数据成员时可以直接初始化。但是静态数据成员不可以。
静态数据成员是属于一个类的。要在定义对象前进行初始化。
初始化格式:
数据类型 类名::静态数据成员名=初始值
注:初始化时不论静态数据成员属于公有、私有、保护,均是这样初始化。
若是静态数据成员属于公有成员,则在类外,可通过(1)类名::静态数据成员名(2)对象名.静态数据成员名
(3)对象指针->静态数据成员名 这三种进行访问。(公有才可)
(4)静态成员函数也可在类内定义。
附上例子:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Score{
public:
Score(int m,int f);
~Score();
static void show();
private:
int mid_exam;
int fin_exam;
static int count;
static float sum;
static float ave;
};
Score::Score(int m,int f){
mid_exam=m;
fin_exam=f;
++count;
sum=sum+fin_exam;
ave=sum/count;
}
Score::~Score(){
}
int Score::count=0;
float Score::sum=0;
float Score::ave;
void Score::show() {
cout<<count<<endl;
cout<<sum<<endl;
cout<<ave<<endl;
}
int main(){
Score rec[3]={
Score(80,88),
Score(90,92),
Score(70,80)
};
rec[3].show() ;
Score::show() ;
return 0;
}
静态成员函数与非静态成员函数的重要区别是:非静态成员函数有 this 指针,而静态成员函数没有 this 指针。
6.友元函数
友元函数既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数(友元类)。
友元函数不是当前类的成员函数,但它可以访问该类所有的成员。
友元函数的声明可以放在公有部分,也可以放在保护部分和私有部分。
7.类的组合:在一个类中内嵌另一个类的对象作为数据成员。(对象成员)
初始化列表进行初始化。
8.当一个类A的成员函数作为另一个类B的友元函数时。
类A要先定义(即声明)。
并且这个友元函数的函数体只能写在这两个类的外部。附上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Score;
class Student{
public:
Student(string na,int num){
name=na;
number=num;
}
void showScore_Student(Score &sc);
private:
string name;
int number;
};
class Score{
public:
Score(int m,int f){
mid_exam=m;
fin_exam=f;
}
friend void Student::showScore_Student(Score &sc);
private:
int mid_exam;
int fin_exam;
};
void Student::showScore_Student (Score &sc){//友元函数
cout<<name<<endl;
cout<<number<<endl;
cout<<sc.mid_exam<<endl;
cout<<sc.fin_exam<<endl;
}
int main()
{
Score score1(90,92);
Student stu1("kiii",2369);
stu1.showScore_Student(score1);
return 0;
}
9.当一个类A作为另一个类B的友元类时。
要先声明类A。
附上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Score;
class Student;
class Score{
public:
Score(int m,int f){
mid_exam=m;
fin_exam=f;
}
friend Student;
private:
int mid_exam;
int fin_exam;
};
class Student{
public:
Student(string na,int num){
name=na;
number=num;
}
void showScore_Student(Score &sc);
private:
string name;
int number;
};
void Student::showScore_Student (Score &sc){
cout<<name<<endl;
cout<<number<<endl;
cout<<sc.mid_exam<<endl;
cout<<sc.fin_exam<<endl;
}
int main()
{
Score score1(90,92);
Student stu1("kiii",2369);
stu1.showScore_Student(score1);
return 0;
}
//这样也可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Score;
class Student{
public:
Student(string na,int num){
name=na;
number=num;
}
void showScore_Student(Score &sc);
private:
string name;
int number;
};
class Score{
public:
Score(int m,int f){
mid_exam=m;
fin_exam=f;
}
friend Student;
private:
int mid_exam;
int fin_exam;
};
void Student::showScore_Student (Score &sc){
cout<<name<<endl;
cout<<number<<endl;
cout<<sc.mid_exam<<endl;
cout<<sc.fin_exam<<endl;
}
int main()
{
Score score1(90,92);
Student stu1("kiii",2369);
stu1.showScore_Student(score1);
return 0;
}
//这样也可,区别在函数体的定义
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Score;
class Student;
class Score{
public:
Score(int m,int f){
mid_exam=m;
fin_exam=f;
}
friend Student;
private:
int mid_exam;
int fin_exam;
};
class Student{
public:
Student(string na,int num){
name=na;
number=num;
}
void showScore_Student(Score &sc){
cout<<name<<endl;
cout<<number<<endl;
cout<<sc.mid_exam<<endl;
cout<<sc.fin_exam<<endl;
};
private:
string name;
int number;
};
//void Student::showScore_Student (Score &sc){
// cout<<name<<endl;
// cout<<number<<endl;
// cout<<sc.mid_exam<<endl;
// cout<<sc.fin_exam<<endl;
//}
int main()
{
Score score1(90,92);
Student stu1("kiii",2369);
stu1.showScore_Student(score1);
return 0;
}
10.类的声明、实现和应用可放在一个文件中,但在实际程序设计中,通常可以划分为3个文件:类声明文件、类实现文件和类使用文件。4.8
11.继承中的访问声明。
附上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Score{
public:
Score(int m,int f){
mid_exam=m;
fin_exam=f;
}
void show(){
cout<<mid_exam<<endl;
cout<<fin_exam<<endl;
}
private:
int mid_exam;
int fin_exam;
};
class Student:private Score{
public:
Student(int m,int f,string na,int num):Score(m,f)
{
name=na;
number=num;
}
void sho(){
cout<<name<<endl;
cout<<number<<endl;
};
Score::show ;
private:
string name;
int number;
};
int main()
{
Student stu1(90,92,"kiii",2369);
stu1.sho() ;
stu1.show() ;//这样进行调用
//stu1.Score::show(); 这样不行,相当于调用其基类的成员函数(私有)
return 0;
}
11.指向基类的对象的指针可以指向派生类(公有)的对象,它可以访问派生类的对象中由基类继承下来的成员,但不可访问派生类新增成员。
12.虚函数的定义
程序在基类中显式定义了虚函数。C++规定,如果在派生类中,没有用 virtual 显式地给出虚函数声明,这时系统就会遵循以下的规则来判断一个成员函数是不是虚函数:
(1)该函数与基类的虚函数有相同的名称。
(2)该函数与基类的虚函数有相同的参数个数及相同的对应参数类型。
(3)该函数与基类的虚函数有相同的返回类型或者满足赋值兼容规则的指针、引用型的返回类型。
派生类的函数满足了上述条件,就被自动确定为虚函数。
注:内联函数不能是虚函数。
构造函数不能是虚函数,但是析构函数可以是虚函数,而且通常说明为虚函数。
13.虚析构函数
虽然派生类的析构函数与基类的析构函数名字不相同,但是如果将基类的析构函数定义为虚函数,由该基类派生的所有派生类的析构函数也都自动成为虚函数。
14.运算符重载
等号运算符的重载只能用于同一类对象的赋值。
加号运算符的重载可以适用于不同类对象的操作。
友元函数重载 ‘++’ 运算符 (附上代码)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Coord{
public:
Coord(int i=0,int j=0);
void display();
friend Coord& operator++(Coord &op);
friend Coord operator++(Coord &op,int);
private:
int x,y;
};
Coord::Coord(int i,int j){
x=i;y=j;
}
void Coord::display() {
cout<<x<<endl;
cout<<y<<endl;
}
Coord& operator++(Coord &op){//前置++返回类型为引用
++op.x;
++op.y;
return op;
}
Coord operator++(Coord &op,int){//返回类型不是引用
Coord op1=op;//利用拷贝构造函数
(op.x)++;
(op.y)++;
return op1;
}
int main()
{
int a=2;
//(a++)++;//这样操作不行,有error
(++a)++;//这样可以
//通过这个去理解类的前置++和后置++
cout<<a<<endl;
Coord ob(11,22);
ob.display() ;
++ob;
ob.display() ;
(++ob)++;//可以
ob.display() ;
ob++;
ob.display() ;
operator++(ob,0);
ob.display() ;
return 0;
}
成员函数重载 ‘++’ 运算符(附上代码)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class Coord{
public:
Coord(int i=0,int j=0);
void display();
Coord& operator++();
Coord operator++(int);
private:
int x,y;
};
Coord::Coord(int i,int j){
x=i;y=j;
}
void Coord::display() {
cout<<x<<endl;
cout<<y<<endl;
}
Coord& Coord::operator++(){//前置++返回类型为引用
++x;
++y;
return *this;
}
Coord Coord::operator++(int){//返回类型不是引用
Coord op1=*this;//利用拷贝构造函数
x++;
y++;
return op1;
}
int main()
{
int a=2;
//(a++)++;//这样操作不行,有error
(++a)++;//这样可以
//通过这个去理解类的前置++和后置++
cout<<a<<endl;
Coord ob(11,22);
ob.display() ;
++ob;
ob.display() ;
(++ob)++;//可以
ob.display() ;
ob++;
ob.display() ;
ob.operator++(0);
ob.display() ;
return 0;
}
加号运算符重载:友元函数和成员函数的区别
友元函数(附上代码)两个参数可为同一类的对象或不同类的对象,因为友元函数可以通过参数来访问对象的每个成员(包括私有成员),操作起来更方便。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class B;
class A{
int x,y;
public:
A(int a,int b){
x=a;y=b;
}
void print(){
cout<<x<<endl;
cout<<y<<endl;
}
friend B operator+(A &a,B &b);
};
class B{
int x,y;
double z;
public:
B(int a,int b,double c){
x=a;y=b;z=c;
}
B(){
}
void print(){
cout<<x<<endl;
cout<<y<<endl;
cout<<z<<endl;
}
friend B operator+(A &a,B &b);
};
B operator+(A &a,B &b){
B temp;
temp.x=a.x+b.x;
temp.y=a.y+b.y;
temp.z=b.z;
return temp;
}
int main()
{
A ob1(12,13);
B ob2(15,18,15.5);
B ob3;
ob3=operator+(ob1,ob2);
ob3.print() ;
return 0;
}
成员函数(附上代码),操作对象一般为同一类的,这样对象的私有成员等才能直接访问。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
class B;
class A{
public:
int x,y;
A(int a,int b){
x=a;y=b;
}
void print(){
cout<<x<<endl;
cout<<y<<endl;
}
};
class B{
int x,y;
double z;
public:
B(int a,int b,double c){
x=a;y=b;z=c;
}
B(){
}
void print(){
cout<<x<<endl;
cout<<y<<endl;
cout<<z<<endl;
}
B operator+(A &a);//不同类的操作对象,类A的数据成员声明为公有才可
B operator+(B &b);
};
B B::operator+(A &a){
B temp;
temp.x=a.x+x;
temp.y=a.y+y;
temp.z=z;
return temp;
}
B B::operator+(B &b)
{
B temp;
temp.x =x+b.x;
temp.y =y+b.y;
temp.z =z+b.z;
return temp;
}
int main()
{
A ob1(12,13);
B ob2(15,18,15.5);
B ob(12,16,10.5);
B ob3;
ob3=ob2+ob1;//注意ob2在前。ob1+ob2就不行
//等价于ob3=ob2.operator+(ob1);
ob3.print() ;
ob3=ob+ob2;
ob3.print() ;
return 0;
}
运算符重载应该注意的几个问题:
1.C++语言中只能对已有的C++运算符进行重载。
2.C++中绝大部分的运算符允许重载,不能重载的运算符只有以下几个:
. 成员访问运算符
.* 成员指针访问运算符
:: 作用域运算符
sizeof 长度运算符
?: 条件运算符
3.重载不能改变运算符的操作对象的个数。
4.重载不能改变运算符原有的优先级。
5.重载不能改变运算符原有的结合特性。
6.运算符重载函数的参数至少应有一个是类对象(或类对象的引用)。也就是说,运算符重载的参数不能全部是C++预定义的基本数据类型。例如,以下定义运算符重载函数的方法是错误的:
int operator+(int x,int y){
return x+y; }
7.双目运算符一般被重载为友元运算符重载函数或成员运算符重载函数,但有一种情况,必须使用友元函数。
ob2=200+ob1;
15.赋值运算符 “=” 的重载
如果没有用户自定义的赋值运算符函数,那么系统将自动地为其生成一个默认的赋值运算符函数实现对象的赋值。
采用默认的赋值运算符函数是一种浅层拷贝的方法。注意:string 类就不会出现浅拷贝的问题。
16.赋值运算符=、下标运算符[ ]、函数调用运算符()、运算符->等 不能定义为友元运算符重载函数,只能作为成员函数。
[ ]双目运算符 X[Y]; ()双目运算符 X(Y)
17.重载插入运算符和提取运算符(双目运算符)
定义的插入运算符 “<<” 重载函数的一般格式如下:
ostream& operator<<(ostream& out,自定义类名& obj){
out<<obj.item1;
...
out<<obj.itemn;
return out;
}
同理,提取运算符 “>>” 重载函数
注意:与插入运算符重载函数一样,提取运算符重载函数也不能是所操作的类的成员函数,但可以是该类的友元函数或普通函数。
18.题目总结:
常数据成员必须进行初始化,并且不能被更新。
常数据成员不可以在定义时直接初始化。
类与类之间的友元关系不可以继承。
内联函数可以进行异常接口声明。
构造函数可以是内联函数。
不能在类的声明中给数据成员赋初值。
数据成员的数据类型不能是register(寄存器类型)
19.
答案是A
20.
构造函数可以是私有的。
具体看博客:https://blog.youkuaiyun.com/simon_2011/article/details/78210222
21.
#include<iostream>
#include<cmath>
using namespace std;
class A{
public:
virtual void f(){
cout<<"fun() of A."<<endl;
}
};
class B:public A{
public:
void f(){
cout<<"fun() of B."<<endl;
}
};
class C:public B{
public:
void f();
};
void C::f(){
A::f() ;
B::f();
cout<<"fun() of C."<<endl;
}
void show(A &ra){
ra.f();
}
int main(){
A a;B b;C c;
show(a);
show(b);
show(c);
return 0;
}
运行结果:
22.虚析构函数的使用
#include<iostream>
#include<cmath>
using namespace std;
class Base{
public:
virtual ~Base(){//虚析构函数
cout<<"Destructor Base"<<endl;
}
//若不采用将基类的析构函数声明为虚析构函数,则delete pBase 时,只会调用基类的析构函数。
};
class Derived:public Base{
public:
~Derived(){
cout<<"Destructor Derived"<<endl;
}
};
int main()
{
Base *pBase=new Derived;//用运算符new为派生类的无名对象动态的
//分配了一个存储空间,并将地址赋给对象指针p
delete pBase;//用运算符delete撤销无名对象,释放动态存储空间
//若不用delete运算符释放,则该空间不会释放。
cout<<endl<<endl;
Base p;
Derived q;
return 0;
}
运行结果: