一、类
1.定义成员函数
输入年份判断是否是闰年,若是输出年份;若不是,输出NO
cpp
#include<iostream>
#include<cstring>
using namespace std;
class TDate{
private:
int month;
int day;
int year;
public:
TDate(int y,int m,int d){
year = y;
month = m;
day = d;
}
void IsLeapYear(){
if((year%400 == 0 )||(year%4==0&&year%100!=0)) {
this->Print() ;
}
else cout<<"No"<<endl;
}
void Print(){
cout<<"Yes"<<endl;
cout<<year<<"/"<<month<<"/"
<<day<<endl;
}
};
int main(){
int y,m,d;
cin>>y>>m>>d;
TDate s(y,m,d);
s.IsLeapYear();
return 0;
}
2.定义头文件,使用类的接口
成员函数定义看作类的内部,如private的变量定义和public成员函数的声明
c
//文件名:tdate。h
//预编译处理器
#ifndef TDATE//if not define TDATE将会定义
#define TDATE//定义TDATE
class TDate{
private:
int year;
int month;
int day;
public:
void Set(int int int);
void IsLeapYear();
void Print();
};
#endif
上述是一个tdate.h的类定义 -> 接口
c
//tdate.cpp 实现类---成员函数的定义
#include<iostream>
#include"tdate.h"//文件名名称
using namespace std;
void TDate::Set(int y,int m,int d){
//....
}
2.调用成员函数
1)指针
c
#include<iostream>
#include"tdate.h"//文件名名称
using namespace std;
void somefuc(TDate *ps){
ps->IsLeapYear();//调用指针输出判断
}
int main(){
TDate s;
s.Set(1983,2,3);
somefuc(&s);
}
2) 引用
cpp
#include<iostream>
#include"tdate.h"//文件名名称
using namespace std;
void somefuc(TDate &ps){
ps->IsLeapYear();//调用指针输出判断
}
int main(){
TDate s;
s.Set(1983,2,3);
somefuc(s);
}
**注意区别指针和引用 大部分情况下是相同 **但指针要给新变量分配空间,而引用直接给原来的地址
3.总结
1.应用1为输入年份,应用2是计算点的直角坐标和极坐标
2.掌握实现类的定义 tdate.h 类的实现 tdate.cpp(成员函数定义)
二、构造函数
1.默认构造函数
cpp
class Student{
private:
char name[10];
};
int main(){
Student s;
}
2.无参构造函数
构造函数可以直接调用print
c
#include<iostream>
#include<cstring>
using namespace std;
class Desk{
private:
int weight;
int high;
int width;
int length;
public:
Desk(){
weight = 20;
high = 10;
width = 90;
length = 100;
print();
}
void print(){
cout<<weight<<" "
<<high<<" "
<<width<<" "
<<length<<endl;
}
};
int main(){
Desk d;
}
3.传参构造函数
见1.1(常见)
4.重载构造函数
1.重载过程中参数个数 要不一样才能构造成功
2.在构造无参传递过程中,不用加(),如
Student s( ); ---error 不用加括号
c
#include<iostream>
#include<cstring>
using namespace std;
class Student{
private:
int grade;
float gpa;
int num;
public:
Student(){
grade = 100;
gpa = 5.0;
num = 123;
cout<<grade<<" "<<gpa<<" "<<num<<" "<<endl;
}
Student(int s,float g){
grade = s;
gpa = g;
num = 456;
cout<<grade<<" "<<gpa<<" "<<num<<" "<<endl;
}
Student(int s){
grade = s;
gpa = 3.6;
num = 789;
cout<<grade<<" "<<gpa<<" "<<num<<" "<<endl;
}
};
int main(){
Student s1;
Student s2(90,4.7);
Student s3(80);
}
5.嵌套构造函数
main函数开始运行,创建Pair对象p,调用Pair构造函数,为private变量分配空间(int变量,Student,Teacher),最后执行自己的public钟的成员函数,
一步一步的执行
**private
成员变量会在 public
成员函数之前分配空间**
c
#include<iostream>
#include<cstring>
using namespace std;
class Student{
private:
int grade;
float gpa;
public:
Student(){
grade = 100;
gpa = 5.0;
cout<<grade<<" "<<gpa<<" "<<endl;
}
};
class Teacher{
private:
int salary;
public:
Teacher(){
salary = 30000;
cout<<salary<<endl;
}
};
class Pair{
private:
int meeting;
//组合
Student s;//创建2
Teacher t;//创建3
public:
Pair(){
meeting = 0;
cout<<meeting<<endl;
}
};
int main(){
Pair p;//创建1
}
6.拷贝构造函数
一般拷贝构造函数的结构
Student (Student &s) //有一个&
1)使用指针
当private里面是指针时,使用new分配空间 strcpy()
c
#include<iostream>
#include<cstring>
using namespace std;
class Person{
private:
char *name;
public:
Person(char *pname){
name = new char[strlen(pname) + 1];
if(pname != 0)
strcpy(name,pname);
cout<<"构造:"<<endl;
}
//自定义拷贝构造函数
Person(const Person &other){
name = new char[strlen(other.name) + 1];
if(other.name != 0 )
strcpy(name,other.name);
cout<<"拷贝:"<<endl;
}
const char* getName( )const{
return name;
}
~Person(){
delete[] name;
}
};
int main(){
Person p1("A");
Person p2(p1);// 等价于p2 = p1;
cout<<p2.getName()<<endl;
}
输出
构造:
拷贝:
A
2)使用数组
直接复制 ,不用分配空间和删除空间 strncpy()
c
#include<iostream>
#include<cstring>
using namespace std;
class Person{
private:
char name[40];
public:
Person(char *pname = "NoName"){
strncpy(name,pname,sizeof(name));
name[sizeof(name) - 1] = '\0';
cout<<"构造:"<<endl;
}
//自定义拷贝构造函数
Person(const Person &other){
strncpy(name,other.name,sizeof(name);
name[sizeof(name) - 1] = '\0';
cout<<"拷贝: "<<endl;
}
const char* getName( )const{
return name;
}
~Person(){
delete name;
}
};
int main(){
Person p1("A");
Person p2(p1);
cout<<p2.getName()<<endl;
}
3) 区别strcpy和strncpy
strcpy () 完整的将数组进行复制
strncpy()对于数组中位置多的元素,使用'\0' 进行填充,否则就全部复制,但常常与 name[sizeof(name) - 1 ] = '\0 连用,保证结尾为空字符,字符串完整
7.关于类的数据成员初始化
对于类的数据成员是一般变量的情况,放在冒号后放在函数体中的初始化是一样的
1)赋值操作有两次初始化
class Myclass{
private:
int d;
public:
Myclass(int i){
d = i;
}
};
2)成员初始化列表
class Myclass{
private:
int d;
public:
//成员初始化列表
//d 在构造函数体执行之前就已经被初始化为 i
Myclass(int i) : d(i){
}
};
常常使用于常量和引用变量,因为
它们必须再被声明的同时进行初始化,它们**不能**在之后被赋值。这意味着你不能先声明一个常量或引用变量,然后再给它赋值
class MyClass {
private:
const int myConst;
int& myRef;
public:
MyClass(int value, int& refValue) : myConst(value), myRef(refValue) {
// 构造函数体
}
};
相当于进行一次初始化,构造函数的参数值给到后面: 成员的()值中。
class StudentID{
int value;
public:
Student(int id = 0){
value = id;
}
};
class Student{
private:
char name[20];
StudentID id;
public:
Student(char *pname,int ssID = 0):id(ssID){
//...
}
};
三、析构函数
1.析构过程
无返回类型 无参数 生命周期结束后自动调用
析构函数和调用构造函数的相反顺序(栈)
c
#include<iostream>
#include<cstring>
using namespace std;
class Student{
private:
int grade;
float gpa;
public:
Student(){
grade = 100;
gpa = 5.0;
cout<<grade<<" "<<gpa<<" "<<endl;
}
~Student(){
cout<<"销毁学生类"<<endl;
}
};
class Teacher{
private:
int salary;
public:
Teacher(){
salary = 30000;
cout<<salary<<endl;
}
~Teacher(){
cout<<"销毁老师类"<<endl;
}
};
class Pair{
private:
int meeting;
Student s;//创建2
Teacher t;//创建3
public:
Pair(){
meeting = 0;
cout<<meeting<<endl;
}
~Pair(){
cout<<"销毁配对类"<<endl;
}
};
int main(){
Pair p;//创建1
}
输出 :
100 5
30000
0
销毁配对类
销毁老师类
销毁学生类
2.外部定义成员函数
使用 :: 进行设置
cpp
#include<iostream>
#include<cstring>
using namespace std;
class Student{
private:
int grade;
float gpa;
int num;
public:
Student();
void Print();
};
Student::Student(){
grade = 100;
gpa = 4.2;
Print();
};
void Student::Print(){
cout<<grade<<" "<<gpa<<" "<<endl;
}
int main(){
Student s;
}
四、综合应用(1)
1.学生姓名 和 学号构造
cpp
#include<iostream>
#include<cstring>
using namespace std;
class Student{
private:
char name[20];
int num;
public:
Student(char*,int);
void Print();
};
Student::Student(char* pname ,int n){
num = n;
strncpy(name,pname,sizeof(name));
name[sizeof(name) - 1] = '\0';
Print();
};
void Student::Print(){
cout<<name<<" "<<num<<" "<<endl;
}
int main(){
Student s("AAB",1);
}
若构造函数传递过程中不提供任何值,则为默认值和python的函数传递有点像
c
Student::Student(char* pname = "NoName" ,int n = 0){
num = n;
strncpy(name,pname,sizeof(name));
name[sizeof(name) - 1] = '\0';
Print();
};//输出:NOName 0
2,学生姓名 和 学号构(进阶)
定义两个类,进行嵌套使用
cpp
#include<iostream>
#include<cstring>
using namespace std;
int id = 0;
class SId{
private:
int value;
public:
SId(int id = 0){
cout<<"构建值="<<id<<endl;
value = ++id;
}
~SId(){
cout<<"销毁id"<<endl;
value = --id;
}
int GetValue(){
return value;
}
};
class Student{
private:
char name[20];
SId id;//分配变量空间
public:
Student(char*,int);
void Print();
};
Student::Student(char* pname = "NoName" ,int n = 0){
//无参传递为默认值
strncpy(name,pname,sizeof(name));
name[sizeof(name) - 1] = '\0';
SId id(n);//传入值
Print();
};
void Student::Print(){
cout<<name<<" "<<id.GetValue()<<" "<<endl;
}
int main(){
Student s("ABC",2);
}
输出:
构建值=0
构建值=2
ABC 1
销毁id
销毁id
上面的方法,初始化浪费空间,
****(常用)****下面在初始化其他类对象时,直接给其他类变量赋值,不用再次申请多余的变量空间
注意使用一个: 进行初始化对象
#include<iostream>
#include<cstring>
using namespace std;
int id = 0;
class SId{
private:
int value;
public:
SId(int id = 0){
cout<<"构建值="<<id<<endl;
value = ++id;
}
~SId(){
cout<<"销毁id"<<endl;
value = --id;
}
int GetValue(){
return value;
}
};
class Student{
private:
char name[20];
SId id;//分配变量空间
//该处不能初始化其他类的对象
public:
Student(char*,int);
void Print();
};
Student::Student(char* pname = "NoName" ,int n = 0) :id(n){
strncpy(name,pname,sizeof(name));
name[sizeof(name) - 1] = '\0';
Print();
};
void Student::Print(){
cout<<name<<" "<<id.GetValue()<<" "<<endl;
}
int main(){
Student s("ABC",9999);
}
输出:
构建值=9999
ABC 10000
销毁id
五、静态成员变量
静态成员函数和静态成员变量都是属于类 而并非属于 一个对象(类的实例 ) 可以直接通过对类名进行访问
-
声明和定义是分开的。静态成员变量在类的定义中声明 ,在类外部进行初始化。
cpp#include<iostream> #include<cstring> using namespace std; class Student{ static int noOfStudent; char name[40]; public: Student(char* pname="no name"){ strncpy(name,pname,40); name [39] = '\0'; noOfStudent ++;//1 } ~Student(){ cout<<"销毁"<<endl; noOfStudent--; } static int number(){ return noOfStudent; } }; int Student::noOfStudent = 0; void fn(){ Student s1; cout<<"s1构造完毕"<<endl; Student s2; cout<<"s2构造完毕"<<endl; cout<<Student::number()<<endl; } int main(){ fn(); cout<<"主函数输出:"; cout<<Student::number<<endl; }
输出:
s1构造完毕
s2构造完毕
2
销毁
销毁
主函数输出:0
六、友元函数
#include<iostream>
#include<cstring>
using namespace std;
class Person{
private:
int age;
public:
Person(int age){
this->age = age;
}
friend void showAge(Person &p);
};
void showAge(Person &p){
cout<<p.age<<endl;
}
int main(){
Person p(10);
showAge(p);
}
下面两个为使用友元函数和不使用友元函数的对比
c
#include<iostream>
#include<cstring>
using namespace std;
class Animal{
private:
int itsWeight;
int itsAge;
public:
friend void setValue(Animal & ,int);
friend void setValue(Animal& ,int ,int);
};
void setValue(Animal &ta ,int tw)
{
ta.itsWeight = tw;
cout<<ta.itsWeight<<" "<<endl;
}
void setValue(Animal &ta ,int tw ,int tn){
ta.itsWeight = tw;
ta.itsAge = tn;
cout<<ta.itsWeight<<" "<<ta.itsAge<<" "<<endl;
}
int main(){
Animal c1,c2;
setValue(c1,123);
setValue(c2,45,67);
}
增加访问类中保护数据的成员函数
#include<iostream>
#include<cstring>
using namespace std;
class Animal{
private:
int itsWeight;
int itsAge;
public:
Animal(int tw,int ta){
itsWeight = tw;
itsAge = ta;
}
int getWeight(){
return itsWeight;
}
int getitsAge(){
return itsAge;
}
};
int main(){
Animal c1(123,456);
cout<<c1.getitsAge()<<" "<<c1.getWeight()<<" "<<endl;
}
总结
- 使用友元, 可以不用增加访问数据的成员函数
- 直接定义友元使用,可以不考虑private、和protected的性质
七、继承
使用学生类进行大学生和研究生的继承
c
#include<iostream>
#include<cstring>
using namespace std;
class Advisor{
int noOfMeeting;
};
class Student{
char name[40];
float gpa;
public:
Student(char *pname = "NoName"){
strncpy(name,pname,40);
name[39] = '\0';
gpa = 0;
}
void addCourse(int hours,float grade){
gpa = (hours + 1 + grade) / 2;
}
float getGpa(){
return gpa;
}
void Print(){
cout<<name<<" "<<gpa<<endl;
}
};
class GraduateStudent : public Student{
private:
Advisor teacher;
int grade;
public:
int getGrade(){
return grade;
}
};
int main(){
Student s1("Leo");
GraduateStudent gs;
s1.addCourse(2,10.0);
s1.Print();//6
gs.addCourse(1,4.0);
cout<<gs.getGpa()<<endl;//3
gs.Print();//3
}
输出:
Leo 6.5
3
NoName 3
- 1.public继承
- 2.private继承,基类的私有还是私有
- 3.protected的保护还是保护
继承后面可以不看,了解
1.继承和组合
类以另一个类对象作为数据成员------组合
前面代码中学生和老师共同形成Pair已经遇到了
2.多继承
尽可能不使用,容易产生成员模糊性
#include<iostream>
#include<cstring>
using namespace std;
class Bed{
protected:
int weight;
public:
Bed(){
weight = 0;
}
void Sleep(){
cout<<"Sleeping"<<endl;
}
int SetWeight(int i){
weight = i;
return weight;
}
};
class Sofa{
protected:
int weight;
public:
Sofa(){
weight = 0;
}
void Watch(){
cout<<"Watching"<<endl;
}
int SetWeight(int i){
weight = i;
return weight;
}
};
class SleeperSofa : public Bed , public Sofa{
//多继承
public:
SleeperSofa(){
}
void FoldOut(){
cout<<"Folding"<<endl;
}
};
int main(){
SleeperSofa ss;
ss.Watch();
ss.FoldOut();
ss.Sleep();
//都有输出SetWeight,但要指明类别,否则模糊
cout<<ss.Sofa::SetWeight(20);
}
输出:
Watching
Folding
Sleeping
20
3.虚拟继承
还很少见 vritual
区分虚拟函数 和虚拟继承 ,二者无任何联系
家具: 获取重量 设置重量(床、沙发)
床:睡觉
沙发:看电视
沙发床:折叠 (床、沙发)
#include<iostream>
#include<cstring>
using namespace std;
class Furniture{
int weight;
public:
Furniture(){
}
void SetWeight(int i){
weight = i;
}
int GetWeight(){
return weight;
}
};
//虚拟继承
class Bed : virtual public Furniture{
public:
Bed(){
}
void Sleep(){
cout<<"Sleeping"<<endl;
}
};
class Sofa : virtual public Furniture{
public:
Sofa(){
}
void WatchTV(){
cout<<"Watching"<<endl;
}
};
class SleeperSofa : public Bed,public Sofa{
public:
SleeperSofa():Sofa(),Bed(){
//类成员初始化嵌套
}
void FoldOut(){
cout<<"Folding"<<endl;
}
};
int main(){
SleeperSofa ss;
ss.FoldOut();
ss.WatchTV();
ss.Sleep();
ss.SetWeight(20);
cout<<ss.GetWeight()<<endl;
}
4.继承、组合、虚拟的构造顺序
先虚拟后对象,再自己
八、多态
c
#include<iostream>
#include<cstring>
using namespace std;
class Base{
public:
virtual void fn(){
cout<<"Base"<<endl;
}
};
class SubClass : public Base{
public:
virtual void fn(){//派生类中的virtual可以省略
cout<<"Subclass"<<endl;
}
};
int i = 0;
void Test(Base & b){
b.fn();
i++;
cout<<"创建完毕"<<i<<endl;
}
int main(){
Base bc;
SubClass sc;
Test(bc);
Test(sc);
}
输出
Base
创建完毕1
Subclass
创建完毕2
多态效果 :virtual 虚函数与成员函数完全相同
纯虚函数:被标明,但不具体实现的虚成员函数
virtual void init() = 0
九、运算符重载
1、一元运算符重载(++)
class Counter {
private:
int count;
public:
Counter(int c = 0) : count(c) {}
// 前缀自增运算符重载
Counter& operator++() {
++count;
return *this;
}
// 后缀自增运算符重载
Counter operator++(int) {
Counter temp(*this);
count++;
return temp;
}
void display() const {
cout << "Count: " << count << endl;
}
};
int main() {
Counter c;
++c; // 前缀自增
c.display(); // 输出: Count: 1
c++; // 后缀自增
c.display(); // 输出: Count: 2
return 0;
}
this指针指向当前函数
2.二元运算符重载(+)
c
class Complex {
private:
double real, imag;
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
Complex operator+(const Complex& c) const {
return Complex(real + c.real, imag + c.imag);
}
void display() const {
cout << real << " + " << imag << "i" << endl;
}
};
int main() {
Complex c1(1.0, 2.0);
Complex c2(3.0, 4.0);
Complex c3 = c1 + c2;
c3.display(); // 输出: 4 + 6i
return 0;
}
++当二元运算符作为成员函数重载时,第一个操作数是调用该函数的对象,第二个操作数通过参数传递。++
3、赋值运算符重载(=):
c
class MyString {
private:
char* str;
public:
MyString(const char* s = "") {
if (s) {
str = new char[strlen(s) + 1];
strcpy(str, s);
} else {
str = nullptr;
}
}
~MyString() {
delete[] str;
}
MyString& operator=(const MyString& s) {
if (this != &s) {
delete[] str;
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
}
return *this;
}
void display() const {
if (str)
cout << str << endl;
else
cout << "Empty string" << endl;
}
};
int main() {
MyString s1("Hello");
MyString s2;
s2 = s1;
s2.display(); // 输出: Hello
return 0;
}