目录
一、struct 和 class 的区别
首先,在C中和C++中,struct(结构体)的情况是有所不同的:
struct的用法:
1.在C语言中,结构体不能为空;
2.在C语言中,struct只是一个结构体名,在C++中是一个类型
3.在C++中,如果struct是空,则大小为1个字节
4.struct内存对齐
5.在C的结构体中,不能写函数,在C++中struct中可以写函数
struct和class的区别:
1.struct是一个值类型,class是一个引用类型
2.在C++中,struct默认是公有的(public),class默认是私有的(private)
3.struct不能被继承,class可以被继承
二、面向对象和面向过程的区别
面向对象就是对实物进行抽象化,而面向过程就是自底向上的编程过程
面向过程的性能比面向对象高,因为类在调用过程中需要实例化,消耗资源较大;但没有面向对象易维护、易复用、易扩展,可维护性差,不易修改;
面向对象易维护、易复用、易扩展,面向对象由封装、继承、多态的特性,可以设计出耦合度低的系统,使系统更加灵活,更易维护;缺点是性能比面向过程低。
三、描述面向对象的特征
封装、继承、多态
封装:把数据(属性)和函数(操作、方法)和成一个整体,使用类和对象实现
继承:继承和派生,两面一体,子类继承了基类;基类派生了子类
多态:由继承产生的相关而不同的类,其对象对相同的消息会产生不同的反应
四、内联函数的宏定义的区别
(1)内联函数在编译时展开,带参的宏定义在预编译时展开
(2)内联函数直接嵌入到目标代码中,带参的宏是简单的做文本替换
(3)内联函数有类型检测、语法判断等功能,宏只是替换
五、引用和指针的区别
指针:是一种数据类型,对于一个类型T,T*就是指向T类型的指针类型,T*这个变量保存了一个 T类型变量的地址
引用:引用是一个对象的别名,与原本的对象没有区别(只是给它起了一个另外的名字)
区别:1.指针可以为空,引用不可以为空,不存在空引用
引用是对象的别名,对象不存在,就不可能有别名
2.引用不能改变指向,而指针可以改变指向,指向别的对象
3.引用的大小是它所指向变量的大小,引用只是一个别名,与原本的对象没有区别;指针是指针本身的大小,(32位4字节,68位8字节)
4.指针有多级指针,引用没有多级引用
六、函数重载
1.函数名相同
2.参数列表不同
3.不能仅通过函数返回值来区分
4.const函数可以构成重载
成员函数后面可以加const——称作常成员函数
七、类和对象的区别
类是抽象的,不占内存,对象是具体的,占用内存空间
八、一个类会默认生成几个函数?
8个,暂时只需要6个
1.构造函数;2.析构函数;3.拷贝构造函数;4.赋值运算符重载函数;5.取地址操作符的重载函数
6.const修饰的取地址操作符的重载函数
九、构造函数和析构函数
构造函数:
为什么需要构造函数?
需要一个函数来初始化数据成员,自己定义的成员函数需要调用才能使用,会滞后(已经有了人,才有年龄,不符合实际),而构造函数会在生成对象的同时初始化数据成员
当对象创建时,自动的调用本类的构造函数
1.构造函数是一个特殊的成员函数,函数名与类名相同,无返回类型,可以带参,可以重载
2.构造函数是一个特殊的公有函数,没有特殊情况,必须是公有的(public)
3.在对象出来前自动调用,而且在生存期中只调用一次
如果程序员没有定义,则类会生成一个默认的构造函数(无参)
如果需要给数据成员开辟空间,需要自己定义构造函数,此时不会生成默认构造函数
在main前可以运行一段代码吗?
可以
遇到对象要调构造函数,所以定义一个全局变量,可以说明在main()函数前
可以运行一段代码
全局变量在main()函数前就要分配空间,同时调用构造函数
调用构造函数3步骤
1.传参
2.根据数据成员在类里面的声明顺序(开辟空间)用冒号语法后面的值进行初始化
3.执行构造函数的函数体
析构函数:
析构函数:
1.析构函数名字与类名相同,在前面加字符“~”
2.析构函数无返回类型
3.析构函数不能带参,不能重载,一个类有且只有一个析构函数
4.对象注销时(生存期结束时),系统会自动调用析构函数
5.如果程序员没有提供,系统会提供一个默认的析构函数:
如果在构造函数中有内存开辟,要手动释放,必须写析构函数
如果没有内存的手动开辟,可以不写析构函数(但一般都要写)
6.执行顺序:析构函数的执行顺序与构造函数相反,先构造的后析构
十、初始化列表(冒号语法)
冒号语法:成员初始化列表
能等号赋值的,就可以用冒号语法
不是数组(拷贝)指针(动态的开辟空间)就可以使用冒号语法
十一、缺省函数:带默认值的函数
一般情况下,函数调用时实参个数与形参相同,为了方便使用函数,定义了具有缺省参数的函数,这种函数调用时,实参个数可以与形参不同
缺省参数指在定义函数时为形参指定缺省值(默认值)
void Fun (int a, int b = 23 ,int c = 80)
缺省函数的调用:
1.如果给出了实参值,将实参传给形参进行调用
2.如果不给出实参,则按缺省值进行调用
3.缺省实参可以是一个表达式,在函数被调用时改表达式被求值(表达式必须有意义)
4.缺省实参可以通过函数调用给出
int my_rand()
{
srand(time(NULL));
int ra = rand() % 100;
return ra;
}
void fun(int a, int b = my_rand())
{
cout << " a = " << a << " b = " << b << endl;
}
void main()
{
fun(12);
}
缺省参数可以有多个,但所有的缺省参数必须放在参数表的右侧(先定义所有的非缺省参数,再定义缺省参数,这是因为在函数调用时,参数自左向右组个匹配)
void fun(int a, int b= 23 ,int c = 8000)
{
cout << "a = " << a << " b = " << b << " c = "<< c << endl;
}
void main()
{
fun(12);
fun(10,20);
fun(10,20,30);
fun(10, , 30);//error
}
十二、拷贝构造函数
拷贝构造函数——复制构造
作用:用已有对象的数据成员去依次初始化新对象的数据成员
如果不写拷贝构造函数,会生成默认拷贝构造函数,
默认拷贝构造函数:把我的拷贝一份去构造你,结果一样
分为深拷贝和浅拷贝
浅拷贝:用已有对象去构造(初始化)新对象
深拷贝:资源的拷贝,给新对象的指针开辟和原对象开辟的一样的空间(拷贝资源)
class Student
{
public:
Student(int num = 1001, const char* name = " "):m_num(num)
{
m_nanme = new char[strlen(name)+1];
strcpy(m_name, name);
}
//浅拷贝
Student(const Student& s) :m_num(s.m_num), m_name(s.m_name)
{
cout << "Student(s)" << endl;
}
//深拷贝
Student(const Student& s) :m_num(s.m_num), m_name(s.m_name)
{
m_name = new char[strlen(s.m_name) + 1];
strcpy(m_name, s.m_name);
}
};
调用拷贝构造函数的三种情况:
1.用一个原有的对象去初始化一个新对象
2.函数传参是类类型的值传递(指针和引用不用调用拷贝构造函数),用一个类去拷贝另一个类
实参->形参(函数的形式参数)
类类型:class A
void fn(A s)//类类型,s是形参
3.函数返回值是类类型的值类型
class A
{
public:
A(int i = 0) :m_i(i) { cout << "A" << m_i << endl; }//构造函数
A(const A& s) :m_i(s.m_i) { cout << "A(A)" << s.m_i << endl; }//拷贝构造
~A() { cout << "~A" << endl; }//析构函数
private:
int m_i;
};
void fn(A s)
{
cout << "fn" << endl;
}
A text()
{
A tt(30);
return tt;//第3种
/*
但返回值为类类型,从函数中先到一个临时空间,然后到主函数中,
只有在从函数到临时空间时的旧的到新的,调用拷贝构造函数,
但从临时空间到主函数时,编译器自动进行了优化,不调用拷贝构造函数
*/
}
int main()
{
A a(10);
A b(a);//第1种
fn(b);//传参,第2种
cout << "111" << endl;
A s = text();
cout << "222" << endl;
}
十三、new、delete和malloc、free的区别
new和 delete:运算符,new既能开辟空间,又能构造对象
new和delete可以重载
new int //开辟一个存放整数的存储空间
new int(100)//同上,并制定该整数的初始值是100
new char[100]//开辟一个存放字符的数组(100个字符)
new int[4][5]//开辟一个存放二维数组的空间,返回首元素地址
float *p=new float(3.14157)//开辟一个存放单精度的空间,并指向该数的初值3.14157
//指针p存放在栈中,但指向的空间在堆中,new出的空间位于堆中
int* p = new int.
delete p;
char* p = new char;
delete p;
int* p = new int[100];
delete []p;
malloc和 free:是开辟和释放空间库函数,malloc只开辟空间,不能构造对象
malloc和free不能重载
#include<stdio.h>
#include<stdlib.h>
void main()
{
int* p;
p = (int*)malloc(sizeof(int)*100);//分配100个int型字节的内存
Student* p = (Student*)malloc(sizeof(Student));//开辟了一个Student类大小的空间
//…………
free(p)
}
总结:1.new和delete会自动调用对象的构造函数和析构函数,malloc和free不会
2.new和delete是C++的运算符,并且可以进行重载;malloc和free是C/C++的库函 数,不能进行重载
十四、static静态数据成员和static成员函数
不同对象如何共享数据?
每个对象都有自己的数据成员,不同对象的数据成员无法共享
全局函数:谁都可以访问,不安全
静态数据成员:可以实现数据共享,该类的所有的对象都共享这块静态存储空间,它不属于某一 个对象,被所有对象共享
静态数据成员存储在静态存储空间,生命周期与整个程序相同,即使某个对象消亡了,它依然存在
静态数据成员的初始化:
在类内定义,类外声明;
静态数据成员不能直接初始化 错误:static int a = 5;
#include <iostream>
using namespace std;
class A
{
public:
static int i;
};
int A::i = 9;
void main()
{
A a;
A b;
cout << a.i <<endl;//9
cout << b.i <<endl;//9
b.i = 8;
cout << a.i <<endl;//8
cout << b.i <<endl;//8
}
静态成员函数:
1.静态成员函数的作用是处理静态数据成员
2.静态成员函数只能访问static成员,包括数据成员和成员函数
3.非static成员函数既可以访问static数据成员,也可以访问非static数据成员。static成员函数只能访问static成员
4.静态成员函数不与任何对象绑定在一起,不包含this指针
1.静态成员函数的作用是处理静态数据成员
2.静态成员函数只能访问static成员,包括数据成员和成员函数
#include <iostream>
using namespace std;
class Box()
{
public:
static int c;
int a,b;
Box(int x,int y)
{
a = x;
b = y;
}
static void fun()
{
cout << c << endl;//不能调用a或者b,只能调用static
cout << " static fun ---" <<endl;
}
};
int Box::c = 8;
void main()
{
Box box (2 ,3);
box.fun ();
Box:: fun();
}
3.非static成员函数既可以访问static数据成员,也可以访问非static数据成员。static成员函数只能访问static成员
#include <iostream>
using namesapce std;
class Box
{
public:
static int c;
int a,b;
Box(int x,int y)
{
a = x;
b = y;
}
void fun()
{
cout << a << " " << c << endl;
//fun是非静态数据成员函数,可以调用静态数据成员,也可以调用非静态数据成员
}
};
int Box::int c = 8;
void main()
{
Box box(2, 3);
box.fun();//通过对象,引用名
}
4.静态成员函数不与任何对象绑定在一起,不包含this指针
十五、友元
友元:让一个函数可以访问私有的成员friend
1.一个普通函数作为类的友元,让函数可以访问私有数据成员
2.一个类的成员函数成为另一个类的成员函数
A::fn(){}想要访问B的私有数据成员,让fn成为B的友元(fn的定义要在B之下,声明可以在前面)3.友元类:一个类的成员函数可以作为另一个类的友元函数
如果当前类的所有成员函数都要访问另一个类,让当前类成为另一个类的友元
友元的特点:
1.不具有对称性
2.不具有传递性
3.不具有继承性
1.一个普通函数作为类的友元,让函数可以访问私有数据成员
class A
{
public:
A(int i=0,int j=0):m_i(i),m_j(j){}
const int& GetI() { return m_i; }
const int& GetJ() { return m_j; }
friend void Add(A& a, A& b);
private:
int m_i;
int m_j;
};
void Add(A& a, A& b)
{
int i = a.m_i + b.m_i;
int j = a.m_j + b.m_j;
cout << i << " " << j << endl;
}
void main()
{
A a(10, 10);
A b(20, 20);
Add(a, b);
}
2.一个类的成员函数成为另一个类的成员函数
class B;//向前引用声明
class A
{
public:
A(int i = 0) :m_i(i) {}
friend void Add(A& a, B& b);//声明友元函数
private:
int m_i;
};
class B
{
public:
B(int j=0):m_j(j){}
friend void Add(A& a, B& b);//声明友元函数
private:
int m_j;
};
void Add(A& a, B& b)
{
cout << a.m_i + b.m_j << endl;
}
void main()
{
A a(10);
B b(20);
Add(a, b);
}
友元类:一个类的成员函数可以作为另一个类的友元函数
class B;
class A
{
public:
A(int i=0):m_i(i){}
void fn(B& b);//fn是A的成员函数
private:
int m_i;
};
class B
{
public:
B(int j = 0) :m_j(j) {}
friend void A::fn(B& b);
private:
int m_j;
};
void A::fn(B& b)
{
cout << m_i + b.m_j << endl;
}
void main()
{
A a(1);
B b(4);
a.fn(b);
}
十六、运算符重载
1.运算符重载,就是对已有的操作符重新进行定义,简化操作,让已有的运算符适应不同的数据类型;
2.运算符重载就是定义一个函数,在函数体内实现想要的功能,当用到改运算符是,会调用这个函数,运算符重载的本质就是函数重载;
3.不要更改运算符的含义,比如不要把 + 重载成减法(-)的含义
运算符重载
运算符重载能够重载为两种形式
1.重载成为成员函数
2.重载成为友元函数
区别值返回和引用返回:
值返回:不能作为左值,就必须用值返回,a+b不能作为左值,因为a+b存储在临时空间
运算符组成的表达式只能放在=的右边
+,值返回
A operator+(A &b)
{
return m_i+m_j;
}
引用返回:可以作为左值,就可以用引用返回
如果能用引用返回,就不要用值返回
重载前置++(++a),后置++(a++)
class A
{
public:
A(int i = 0):m_i(i){}
A operator+(const A& s)
{
return (m_i + s.m_i);
}
void Print()
{
cout << "i = "<<m_i << endl;
}
friend A operator-(const A& a, const A& b);
A operator++(int)//为了区别前++和后++,让它们构成重载,在后++里写一个int,
{ //不接受任何值,没有什么意思,就是做为区别
return A(m_i++);
}
A& operator++()//前置++,使用,引用返回
{
++m_i;
return *this;
}
private:
int m_i;
};
A operator-(const A& a, const A& b)//重载成友元形式
{
return A(a.m_i - b.m_i);
}
void main()
{
A a(10);
A b(20);
A c;
(a + b).Print();
(b - a).Print();
(a++).Print();
(++a).Print();
++c = b;
c.Print();
}
重载<<
cout是一个对象,ostream类的一个对象
cin是一个对象,istream类的一个对象
class A
{
public:
A(int i = 0) :m_i(i) {}
void Print()
{
cout << "i = " << m_i << endl;
}
A& operator=(const A& s)
{
if (this == &s)
return *this;
m_i = s.m_i;
return *this;
}
friend ostream& operator<<(ostream& os, const A& a);
private:
int m_i;
};
ostream& operator<<(ostream& os, const A& a)//<<输出的运算符不能重载为成员函数,只能重载为友元函数
//因为要写成成员函数,就要更改类,但我们不能改写ostream类
{
os << a.m_i;
return os;
}
int main()
{
A a(10);
A b(20);
/*b = a;
b.Print();*/
(a = b).Print();
cout << a << endl;
return 0;
}
十七、this指针
this指针的作用:指向成员函数所作用的对象
非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针;
静态成员函数是共享的变量,不是属于某个对象的变量,没有this指针
十八、const指针
1.常量指针
const int* p = &a;
指针的指向可以改变,但指针指向的值不可改变
2.指针常量
int* const p = &a;
指针的指向不能改变,指针指向的值可以改变
3.const既能修饰指针,也可以修饰常量
const int* const p = &a;