友元
- 友元包含友元函数和友元类;
- 若将函数A(非成员函数)声明为类C的友元函数,那么函数A就能直接访问类C对象的所有成员;
- 若将类A声明为类C的友元类,那么类A的所有成员函数都能直接类C对象的所有成员;
- 友元破坏了面向对象的封装性,但在某些频繁访问成员变量的地方可以提高性能;
#include <iostream>
using namespace::std;
//class Person {
//
//public:
// //声明并初始化
// int m_age = 0;
// //定义虚函数
// virtual void run(){
//
// }
//
// Person(int age = 0) : m_age(age){
// cout << "Person() " << endl;
// }
//};
class Point {
int m_x;
int m_y;
public:
Point(int x,int y) : m_x(x),m_y(y){ }
int getX() const {
return this->m_x;
}
int getY() const {
return this->m_y;
}
//友元函数
friend Point add(const Point &,const Point &);
//友元类
friend class Math;
};
Point add(const Point &point1,const Point &point2){
return Point(point1.m_x + point2.m_x,point1.m_y + point2.m_y);
}
class Math {
public:
Point sub(const Point &point1,const Point &point2){
return Point(point1.m_x - point2.m_x,point1.m_y - point2.m_y);
}
};
int main(int argc, const char * argv[]) {
Point p1(10,20);
Point p2(20,30);
Point p = add(p1, p2);
return 0;
}
内部类
- 将类A定义在类C的内部,那么类A就是一个内部类;
- 内部类支持public,protected,private权限;
- 成员函数可以直接访问其外部类对象的所有成员,反之不行;
- 成员函数可以直接不带类名,对象名直接访问其外部类的static成员;
- 内部类不会影响外部类的内存布局;
#include <iostream>
using namespace::std;
class Person {
int m_age;
public:
static int ms_legs;
Person(){
cout << "Person()" << endl;
}
//内部类Car
class Car {
int m_price;
public:
Car(){
cout << "Car()" << endl;
}
void run(){
Person person;
person.m_age = 20;
ms_legs = 4;
}
};
};
int Person::ms_legs = 2;
class Point {
public:
//内部类Math
class Math {
public:
void test();
};
};
//内部类声明与实现分离
void Point::Math::test(){
}
int main(int argc, const char * argv[]) {
Person person;
//内部类
Person::Car car;
return 0;
}
局部类
- 在一个函数内部定义的类,称之为局部类;
- 其作用域仅局限于所在的函数内部;
- 其所有的成员必须定义在类的内部,不允许定义static成员变量;
- 成员函数不能直接访问函数内部的局部变量,static变量除外;
#include <iostream>
void test(){
int age = 10;
static int s_age = 30;
//局部类
class Person{
void run(){
s_age = 40;
age = 20; //报错
}
};
}
int main(int argc, const char * argv[]) {
return 0;
}
运算符重载
- 运算符重载:可以为运算符增加一些新的功能;
- 全局函数与成员函数都支持运算符重载;
#include <iostream>
using namespace::std;
class Point {
public:
int m_x;
int m_y;
Point(int x,int y) : m_x(x),m_y(y){ }
void display(){
cout << this->m_x << "-" << this->m_y << endl;
}
friend Point operator+(const Point &,const Point &);
};
//运算符重载
Point operator+(const Point &p1,const Point &p2){
return Point(p1.m_x + p2.m_x,p1.m_y + p2.m_y);
}
int main(int argc, const char * argv[]) {
Point p1(10,20);
Point p2(20,30);
Point p3 = p1 + p2;
p3.display();
return 0;
}
-
operator+
重载 + 运算符,实现两个point对象相加; - 可将
operator+
写成Point类的成员函数如下:
#include <iostream>
using namespace::std;
class Point {
public:
int m_x;
int m_y;
Point(int x,int y) : m_x(x),m_y(y){ }
void display(){
cout << this->m_x << "-" << this->m_y << endl;
}
//运算符重载
Point operator+(const Point &p){
return Point(this->m_x + p.m_x,this->m_y + p.m_y);
}
};
int main(int argc, const char * argv[]) {
Point p1(10,20);
Point p2(20,30);
Point p3 = p1 + p2;
p3.display();
return 0;
}
- 下面实现一个Point类关于运算符重载的封装:
// Point.hpp
// C++34_运算符重载
//
// Created by ljj on 8/15/21.
//
#ifndef Point_hpp
#define Point_hpp
#include <stdio.h>
#include <iostream>
using namespace::std;
class Point {
public:
//友元函数
friend ostream &operator<<(ostream &,const Point &);
int m_x;
int m_y;
//构造函数
Point(int x,int y) : m_x(x),m_y(y){ }
//运算符重载
Point operator+(const Point &p);
Point operator-(const Point &p);
Point &operator+=(const Point &p);
bool operator==(const Point &p);
bool operator!=(const Point &p);
//返回的point对象 不允许赋值
const Point operator-() const;
//前++
Point &operator++();
//后++ 返回值不可修改
const Point operator++(int);
};
#endif /* Point_hpp */
// Point.cpp
// C++34_运算符重载
//
// Created by ljj on 8/15/21.
//
#include "Point.hpp"
//运算符重载
Point Point::operator+(const Point &p){
return Point(this->m_x + p.m_x,this->m_y + p.m_y);
}
Point Point::operator-(const Point &p){
return Point(this->m_x - p.m_x,this->m_y - p.m_y);
}
Point &Point::operator+=(const Point &p){
this->m_x += p.m_x;
this->m_y += p.m_y;
return *this;
}
bool Point::operator==(const Point &p){
return (this->m_x == p.m_x && this->m_y == p.m_y);
}
bool Point::operator!=(const Point &p){
return (this->m_x != p.m_x || this->m_y != p.m_y);
}
//返回的point对象 不允许赋值
const Point Point::operator-() const {
return Point(-this->m_x,-this->m_y);
}
//前++
Point &Point::operator++(){
this->m_x++;
this->m_y++;
return *this;
}
//后++ 返回值不可修改
const Point Point::operator++(int){
Point point(this->m_x,this->m_y);
this->m_x++;
this->m_y++;
return point;
}
//友元函数
ostream &operator<<(ostream &cout,const Point &point){
return cout << point.m_x << "," << point.m_y;
}
// main.cpp
// C++34_运算符重载
//
// Created by ljj on 8/14/21.
//
#include <iostream>
#include "Point.hpp"
using namespace::std;
int main(int argc, const char * argv[]) {
Point p1(10,20);
Point p2(20,30);
Point p3 = p1 + p2;
cout << p3 << endl;
Point p4 = p1 - p2;
cout << p4 << endl;
++p1;
cout << p1 << endl;
p2++;
cout << p2 << endl;
return 0;
}
运算符重载实战
- 自定义一个String字符串类,与C++系统类
string
功能类似;
// String.hpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#ifndef String_hpp
#define String_hpp
#include <stdio.h>
#include <iostream>
using namespace::std;
class String {
friend ostream &operator<<(ostream &,const String &);
private:
//C语言字符串
char *m_cstring;
public:
//单参数构造函数
String(const char *ctring);
~String();
String &operator=(const char *cstring);
};
#endif /* String_hpp */
#include "String.hpp"
String::String(const char *cstring){
cout << "String::String(const char *cstring)" << endl;
if (!cstring) return;
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
}
String::~String(){
cout << "String::~String()" << endl;
if (!this->m_cstring) return;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
String &String::operator=(const char *cstring){
//释放旧的字符串
if (this->m_cstring){
cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
//指向新的字符串
if (cstring){
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
cout << "new [] 指向新的字符串 = " << cstring << endl;
}
return *this;
}
ostream &operator<<(ostream &cout,const String &string){
if (!string.m_cstring) return cout;
return cout << string.m_cstring;
}
第一种调用情况如下:
#include <iostream>
#include "String.hpp"
using namespace::std;
int main(int argc, const char * argv[]) {
//直接调用构造函数
String str1("333");
cout << str1 << endl;
//隐式调用单参数构造函数
String str2 = "123";
cout << str2 << endl;
//隐式调用单参数构造函数
char name[] = "444";
String str3 = name;
cout << str3 << endl;
return 0;
}
- 防止字符串被提前销毁释放,即String实例对象还存在,但字符串内容已经释放,需要将字符串内容拷贝到堆空间,需对
构造函数
与析构函数
作出特殊处理;
第二种调用情况:
#include <iostream>
#include "String.hpp"
using namespace::std;
int main(int argc, const char * argv[]) {
//隐式调用单参数构造函数
String str2 = "123";
cout << str2 << endl;
//重新赋值
str2 = "666";
cout << str2 << endl;
return 0;
}
- str2前后两次赋值,其内存操作如下:
Snip20210815_165.png
-
String str2 = "123"
,隐式调用单参数构造函数,创建一个String实例对象str2,且会将123
字符串内容拷贝到堆区,然后m_cstring
指向堆区字符串123
-
str2 = "666"
,其中"666"
会隐式调用单参数构造函数,创建一个临时的字符串String实例对象,且会将666
字符串内容拷贝到堆区,然后m_cstring
指向堆区字符串666
,最后将str2对象的m_cstring
指针指向堆区字符串666
,那么原来的堆区字符串123
就没有人管理了,直接导致内存泄漏
,为了防止这种情况,下面重载=
运算符,在str2 = "666"
赋值的时候做特殊处理,如下所示:
String &String::operator=(const char *cstring){
//释放旧的字符串
if (this->m_cstring){
cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
//指向新的字符串
if (cstring){
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
cout << "new [] 指向新的字符串 = " << cstring << endl;
}
return *this;
}
- 总结一句话:释放旧的字符串,指向新的字符串;
第三种调用情况:
#include <iostream>
#include "String.hpp"
using namespace::std;
int main(int argc, const char * argv[]) {
//隐式调用单参数构造函数
String str1 = "111";
String str2 = "123";
str1 = str2;
return 0;
}
-
str1 = str2
,两者都是String字符串对象,需重载=
运算符,参数为String字符串的情况;
String &String::operator=(const String &string){
return operator=(string.m_cstring);
}
第四种调用情况:
#include <iostream>
#include "String.hpp"
using namespace::std;
int main(int argc, const char * argv[]) {
//隐式调用单参数构造函数
String str1 = "111";
String str2 = "123";
str1 = str1;
return 0;
}
-
str1 = str1
,赋值操作用的同一个String对象,会调用String &String::operator=(const String &string)
函数,接着会调用String &String::operator=(const char *cstring)
函数,其内存操作如下:
Snip20210815_166.png
- 接下来执行,释放旧的字符串,指向新的字符串,str1对象的
m_cstring
指针清空,并且堆空间的111
字符串释放掉,但参数const char *cstring
指针依然指向堆空间的111
字符串,出现野指针错误,所以要针对 用字符串本身赋值时要做判断处理;
String &String::operator=(const char *cstring){
//指向相同的堆空间 直接返回
if (this->m_cstring == cstring) return *this;
//释放旧的字符串
if (this->m_cstring){
cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
//指向新的字符串
if (cstring){
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
cout << "new [] 指向新的字符串 = " << cstring << endl;
}
return *this;
}
第五种调用情况:
#include <iostream>
#include "String.hpp"
using namespace::std;
int main(int argc, const char * argv[]) {
//隐式调用单参数构造函数
String str1 = "111";
//拷贝构造函数 默认浅拷贝
String str2 = str1;
return 0;
}
- 涉及拷贝构造函数,默认是浅拷贝,需要进行深拷贝;
String::String(const String &string){
// this->m_cstring = string.m_cstring;
operator=(string.m_cstring);
// *this = string.m_cstring;
}
- String类的完整代码如下:
// String.hpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#ifndef String_hpp
#define String_hpp
#include <stdio.h>
#include <iostream>
using namespace::std;
class String {
friend ostream &operator<<(ostream &,const String &);
private:
//C语言字符串
char *m_cstring;
public:
//单参数构造函数
String(const char *cstring);
//拷贝构造函数
String(const String &string);
~String();
//C语言字符串
String &operator=(const char *cstring);
//String字符串
String &operator=(const String &string);
};
#endif /* String_hpp */
// String.cpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#include "String.hpp"
String::String(const char *cstring){
if (!cstring) return;
cout << "String::String(const char *cstring) -- new " << cstring << endl;
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
}
String::String(const String &string){
// this->m_cstring = string.m_cstring;
operator=(string.m_cstring);
// *this = string.m_cstring;
}
String::~String(){
// if (!this->m_cstring) return;
// cout << "String::~String() delete" << this->m_cstring << endl;
// delete [] this->m_cstring;
// this->m_cstring = NULL;
operator=(NULL);
//将NULL赋值给当前对象 等价写法
// *this = NULL;
// (*this).operator=(NULL);
// this->operator=(NULL);
}
String &String::operator=(const char *cstring){
//指向相同的堆空间 直接返回
if (this->m_cstring == cstring) return *this;
//释放旧的字符串
if (this->m_cstring){
cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
//指向新的字符串
if (cstring){
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
cout << "new [] 指向新的字符串 = " << cstring << endl;
}
return *this;
}
String &String::operator=(const String &string){
// return operator=(string.m_cstring);
return *this = string.m_cstring;
}
ostream &operator<<(ostream &cout,const String &string){
if (!string.m_cstring) return cout;
return cout << string.m_cstring;
}
- 将字符串的判断,释放旧字符串,创建新堆空间存放字符串全部封装到一个函数方法
assign
中,进行封装改造:
#ifndef String_hpp
#define String_hpp
#include <stdio.h>
#include <iostream>
using namespace::std;
class String {
friend ostream &operator<<(ostream &,const String &);
private:
//C语言字符串
char *m_cstring;
String &assign(const char *cstring);
public:
//单参数构造函数
String(const char *cstring);
//拷贝构造函数
String(const String &string);
~String();
//C语言字符串
String &operator=(const char *cstring);
//String字符串
String &operator=(const String &string);
};
#endif /* String_hpp */
// String.cpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#include "String.hpp"
String::String(const char *cstring){
assign(cstring);
}
String::String(const String &string){
assign(string.m_cstring);
}
String::~String(){
assign(NULL);
}
String &String::operator=(const String &string){
return assign(string.m_cstring);
}
String &String::assign(const char *cstring){
//指向相同的堆空间 直接返回
if (this->m_cstring == cstring) return *this;
//释放旧的字符串
if (this->m_cstring){
cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
//指向新的字符串
if (cstring){
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
cout << "new [] 指向新的字符串 = " << cstring << endl;
}
return *this;
}
ostream &operator<<(ostream &cout,const String &string){
if (!string.m_cstring) return cout;
return cout << string.m_cstring;
}
- String的完整封装,功能如下:
// String.hpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#ifndef String_hpp
#define String_hpp
#include <stdio.h>
#include <iostream>
using namespace::std;
class String {
friend ostream &operator<<(ostream &,const String &);
private:
//C语言字符串
char *m_cstring = NULL;
String &assign(const char *cstring);
//C语言字符串拼接
char* join(const char *cstring1,const char *cstring2);
public:
//单参数构造函数 默认参数值为空
String(const char *cstring = "");
//拷贝构造函数
String(const String &string);
~String();
//C语言字符串
String &operator=(const char *cstring);
//String字符串
String &operator=(const String &string);
String operator+(const char *cstring);
String operator+(const String &string);
String &operator+=(const char *cstring);
String &operator+=(const String &string);
//返回指定位置的字符
char operator[](size_t index);
bool operator>(const char *cstring);
bool operator>(const String &string);
};
#endif /* String_hpp */
// String.cpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#include "String.hpp"
String::String(const char *cstring){
assign(cstring);
}
String::String(const String &string){
assign(string.m_cstring);
}
String::~String(){
assign(NULL);
}
String &String::operator=(const String &string){
return assign(string.m_cstring);
}
String &String::assign(const char *cstring){
//指向相同的堆空间 直接返回
if (this->m_cstring == cstring) return *this;
//释放旧的字符串
if (this->m_cstring){
cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
delete [] this->m_cstring;
this->m_cstring = NULL;
}
//指向新的字符串
if (cstring){
this->m_cstring = new char[strlen(cstring)+1]{};
strcpy(this->m_cstring, cstring);
cout << "new [] 指向新的字符串 = " << cstring << endl;
}
return *this;
}
char* String::join(const char *cstring1,const char *cstring2){
if (!cstring1 || !cstring2) return NULL;
char *newString = new char[strlen(cstring1) + strlen(cstring2) + 1]{};
strcat(newString, cstring1);
strcat(newString, cstring2);
return newString;
}
String String::operator+(const char *cstring){
String str;
char *newString = join(this->m_cstring, cstring);
if (newString){
//回收str 之前的空的("")堆空间
str.assign(NULL);
str.m_cstring = newString;
}
return str;
}
String String::operator+(const String &string){
return operator+(string.m_cstring);
}
String &String::operator+=(const char *cstring){
char *newString = join(this->m_cstring, cstring);
if (newString) {
this->assign(NULL);
this->m_cstring = newString;
}
//返回当前对象本身 给返回值引用
return *this;
}
String &String::operator+=(const String &string){
return operator+=(string.m_cstring);
}
char String::operator[](size_t index){
if (index < 0 || !this->m_cstring) return '\0';
if (index >= strlen(this->m_cstring)) return '\0';
return this->m_cstring[index];
}
bool String::operator>(const char *cstring){
if (!this->m_cstring || !cstring) return 0;
return strcmp(this->m_cstring, cstring) > 0;
}
bool String::operator>(const String &string){
return operator>(string.m_cstring);
}
ostream &operator<<(ostream &cout,const String &string){
if (!string.m_cstring) return cout;
return cout << string.m_cstring;
}
调用父类的运算符重载函数
// main.cpp
// C++35_运算符重载(String)
//
// Created by ljj on 8/15/21.
//
#include <iostream>
using namespace::std;
class Person {
int m_age;
public:
Person &operator=(const Person &person){
this->m_age = person.m_age;
return *this;
}
};
class Student : public Person {
int m_score;
public:
Student &operator=(const Student &student){
//调用父类的运算符重载函数
Person::operator=(student);
this->m_score = student.m_score;
return *this;
}
};
int main(int argc, const char * argv[]) {
return 0;
}