C++:随笔7---运算符重载
當我們進行a+b操作時,他返回的是a-b。
例子:實現復數的加法。
//用C++類的方法實現復數加法
#include<string>
#include<iostream>class Plus//定義一個復數類
{
public://構造函數有兩種情況,一種是對這個類有初始化;另一種是沒有初始化(沒有初始化的話把他們都賦值為0)Plus();Plus(double r,double i);virtual ~Plus();//析構函數Plus add(Plus &d);//這個方法是實現復數加法的方案,函數返回值是一個類void printf();
private:double real;//實步double imag;//虛步
};
Plus::Plus()
{real = 0;imag = 0;
}Plus::Plus(double r, double i)//如果有初始化的話把兩個參數賦值進兩個變量里邊
{real = r;imag = i;
}Plus::~Plus(){}Plus Plus::add(Plus &d)//參數就是被加數{Plus c;//實例化一個復數類cc.real = real + d.real;//把參數提取進來跟我自身的一個加數(自身的話因為是已經被主函數傳進來的對象進行初始化了),使他們兩個的實部相加c.imag = imag + d.imag;return c;}void Plus::printf(){std::cout << "(" << real << "," <<imag<< "i)" << std::endl;}
int main()
{Plus p0(6, 5);Plus p1(3, 4);//實例化一個對象并初始化Plus p2(5, -8);Plus p3,p4;p3 = p1.add(p2);p4 = p0.add(p3);std::cout << "p1=";p1.printf();std::cout << "p2=";p2.printf();std::cout << "p1+p2=";p3.printf();std::cout << "p0+p1+p2=";p4.printf();return 0;
}
結果:
這是通過定義相關的函數來實現加法的。
運算符重載:
? ??
改寫上述類使用運算符重載實現:
#include<string>
#include<iostream>
//對運算符+進行重載
class Complex//定義一個復數類
{
public://構造函數有兩種情況,一種是對這個類有初始化;另一種是沒有初始化(沒有初始化的話把他們都賦值為0)Complex();Complex(double r,double i);virtual ~Complex();//析構函數Complex operator+(Complex &d);//這個方法是實現復數加法的方案,函數返回值是一個類void printf();
private:double real;//實步double imag;//虛步
};
Complex::Complex()
{real = 0;imag = 0;
}
Complex::Complex(double r, double i)//如果有初始化的話把兩個參數賦值進兩個變量里邊
{real = r;imag = i;
}
Complex::~Complex(){}
Complex Complex::operator+(Complex &d)//比如說a+b,那么他的參數就是b(那么a跑哪里去了為什么只有一個參數)(事實上就是a來調用operator+來加上參數d的){Complex c;//實例化一個復數類cc.real = real + d.real;//把參數提取進來跟我自身的一個加數(自身的話因為是已經被主函數傳進來的對象進行初始化了),使他們兩個的實部相加c.imag = imag + d.imag;return c;}void Complex::printf(){std::cout << "(" << real << "," <<imag<< "i)" << std::endl;}
int main()
{Complex p0(6, 5);Complex p1(3, 4);//實例化一個對象并初始化Complex p2(5, -8);Complex p3,p4;p3 =p1+p2;//為什么可以直接加呢?因為加號在類里面已經被重載了(不用像原來一樣調用函數實現加法了)std::cout << "p1=";p1.printf();std::cout << "p2=";p2.printf();std::cout << "p1+p2=";p3.printf();return 0;
}
結果是一樣的。
????
友元函數(就是說這個函數不屬于這個類,但是是這個類的一個朋友一個親戚,所以它可以訪問這個類的私有成員)(為什么要將運算符函數做為友元函數?因為我們要訪問私有成員不是屬于這個類的函數的話是訪問不到他的私有成員,那怎么辦呢?只能通過他的朋友)
改寫如下:但是這種方法比較不推薦但是大家也得知道。
#include<string>
#include<iostream>
//對運算符+進行重載
class Complex//定義一個復數類
{
public://構造函數有兩種情況,一種是對這個類有初始化;另一種是沒有初始化(沒有初始化的話把他們都賦值為0)Complex();Complex(double r,double i);virtual ~Complex();//析構函數friend Complex operator+(Complex &c,Complex &d);//這個方法是實現復數加法的方案,函數返回值是一個類void printf();
private:double real;//實步double imag;//虛步
};
Complex::Complex()
{real = 0;imag = 0;
}
Complex::Complex(double r, double i)//如果有初始化的話把兩個參數賦值進兩個變量里邊
{real = r;imag = i;
}
Complex::~Complex(){}
//注意這里作為友元函數不屬于Complex,記得別寫::
Complex operator+(Complex &c,Complex &d){return Complex(c.real+d.real,c.imag+d.imag);}void Complex::printf(){std::cout << "(" << real << "," <<imag<< "i)" << std::endl;}
int main()
{Complex p0(6, 5);Complex p1(3, 4);//實例化一個對象并初始化Complex p2(5, -8);Complex p3,p4;p3 =p1+p2;std::cout << "p1=";p1.printf();std::cout << "p2=";p2.printf();std::cout << "p1+p2=";p3.printf();return 0;
}
最后結果一樣。
25-32
--------動態內存管理
首先明白概念,靜態內存分配和動態內存分配分別是什么?
靜態內存分配與動態內存分配
靜態內存分配:全局或局部變量(對象),編譯器在編譯時都可以根據變量或對象的類型知道所需內存空間的大小。從而系統在適當的時候為他們分配內存空間
動態內存分配:有些操作對象只有在程序運行時才能確定,這樣編譯器在編譯時就無法為他們預定存儲空間,只能在程序運行時,系統根據運行時的要求進行內存分配稱為動態內存分配。動態分配都在自由存儲區中進行。(這樣的內存管理才是真正的動態)
用new申請了一個int類型的地址(內存塊,總共是4個字節),把首地址給了這個指針變量,變量名字叫做i。
?
比如我們有一個0到18的地址,棧空間里邊聲明了一個變量叫做i,i是一個指針變量所以他存放的是一個地址,這個地址由哪來呢?由new出了一個int內存塊,返回他的初始地址把他存放進去,初始地址是4,int占用4個字節(4567這四個字節是new出來的內存塊,把他的初始地址4存放在這個指針變量里邊)
delete i;的操作就是把4567這四個內存塊還給我們的操作系統,但是我們的指針變量他并沒有發生改變,因為我們這個指針變量這個i,他事實上是一個在棧里邊生成的局部變量,沒有釋放它,他的地址還是有的,只是把這個地址里邊的內容給釋放開了,所以為了保險起見我們要把i設置為NULL,這樣子的話就變成下圖,i里邊什么都沒有了,現在就是一個廢了的指針,這個指針沒有任何的用處,如果我們試圖對賦給NULL的指針進行解引用的時候呢,那么他就會出錯。
?
?
#include<stdio.h>
#include<iostream>
#include<string>
class Company
{public:Company(std::string theName);virtual void printInfo();protected:std::string name;
};
class TechCompany :public Company
{
public:TechCompany(std::string theName, std::string product);virtual void printInfo();
private:std::string product;
};
Company::Company(std::string theName)
{name = theName;
}
void Company::printInfo()
{std::cout << "這個公司的名字叫:" << name << std::endl;
}
TechCompany::TechCompany(std::string theName, std::string product):Company(theName)
{this->product = product;}
void TechCompany::printInfo()
{std::cout << name << "公司主打產品是:" << product << std::endl;
}
int main()
{Company *company = new Company("apple");company->printInfo();delete company;company = NULL;company = new TechCompany("apple", "蘋果");company->printInfo();delete company;company = NULL;return 0;
}
動態數組:
?
?
刪除一個動態數組:
#include<iostream>
#include<string>
int main()
{unsigned int count = 0;//count是用來動態的定義的std::cout << "請輸入數據元素的個數:" << std::endl;std::cin >> count;//輸入存放到count里面int *x = new int[count];//定義一個指針,用new來對它進行初始化,給他申請內存,這塊的申請內存是在程序運行的時候申請的,而不是編譯的時候申請的,所以我們說他是動態內存,//是從堆里邊申請的,而不是棧里邊,一般我們像變量,局部變量是從棧里邊申請的數據,而這些動態申請呢我們程序由程序在運行的時候申請的,這些是從堆里邊申請的,只是兩塊不同的內存。for (int i = 0; i < count; i++)//初始化{x[i] = i;}for (int i = 0; i < count; i++)//打印{std::cout << "x[" << i << "]=" << x[i] << std::endl;}return 0;
}
? 因為我們是使用new語句分配的,他這塊內存是分配在堆里邊的,不會隨著我們函數的結束而釋放掉。如果是像局部變量這種的,他是在棧里邊由程序自動分配(由編譯器自動分配),他會在函數調用結束自動釋放。
//從函數和方法返回內存
#include<iostream>
int *newInt(int value);//1聲明一個函數,這個函數前邊加一個*號,說明他的返回值類型是int*,是一個指向整型變量的一個地址,一個指針
int main()
{int *x = newInt(20);//2、6定義一個指針變量x,這是一個指向整數的指針變量,他賦值為newInt(20)這個函數的返回值,因為這個函數的返回值是一個地址,//因為聲明函數的時候加了一個*號是一個指針的標志,說明他的返回值是一個地址。調用他std::cout << *x<<std::endl;//7打印地址delete x;//8把他釋放掉x = NULL;//9把他的指針填充NULL,使得指針不會被隨意的亂用return 0;//然后結束整個程序
}
int *newInt(int value)
{int *myInt = new int;//3這個里邊它new一個整型的變量,在堆里邊申請一個整型的內存塊*myInt = value;//4然后給他賦值,賦值為這個參數value的值(*myInt使用*給他解引用)return myInt;//5賦值之后返回他的地址(6處接到地址)
}
?//為什么不讓函數返回一個指向局部變量的指針
傳址調用:
#include<iostream>
void swap(int *x, int *y);
int main()
{int a, b;a = 3;b = 5;swap(&a, &b);//傳引用過去,傳他們的地址過去std::cout << "a=" << a << std::endl;std::cout << "b=" << b << std::endl;return 0;
}
void swap(int *x, int *y)//這里要接的兩個形參定義為指針類型,我們就可以接到他的地址
{
#if 0int temp;temp = *x;*x = *y;*y = temp;
#endif*x ^= *y;*y ^= *x;*x ^= *y;
}
任何一個函數都不應該把他自己的局部變量的指針作為它的返回值,因為局部變量在棧里,函數結束自動會釋放。(因為棧在這個函數結束時會釋放,所以你把他的地址返回那我們在主函數主代碼里邊引用到的地址將是一個變化莫測的值,因為他釋放了,里邊就只有垃圾值,或其他的變量可能就會出現錯誤)
如果你想讓一個函數在不會留下任何隱患的情況下返回一個指針,那他只能是一個動態分配的內存塊的基地址。(因為動態分配,是在堆里邊只有delete語句才能把他釋放。)
PS:強調兩個概念
函數指針:(重在指針兩個字)說明這個函數指針事實上就是一個指針,他就是一個存放地址變量。(指向函數首地址的指針變量)
#include<stdio.h>
int fun(int x, int y);
int main()
{int i, a, b;int(*p)(int a,int b);//聲明函數指針,前邊多了括號(*p)(說明他是一個指針變量,指向函數的指針變量)后邊的(),表明它是一個函數scanf("%d", &a);p = fun;//給函數指針p賦值,使他指向函數fprintf("請輸入個數字:\n");for (i = 0; i < 10; i++){scanf("%d", &b);a = (*p)(a, b);//通過指針p調用函數f。(p來代替fun)}printf("The max number is:%d", a);return 0;
}
int fun(int x, int y)
{int z; z = (x > y) ? x : y;return(z);
}
指針函數:
一個函數可以帶回一個整型數據的值,字符類型值,和實型類型的值,還可以帶回指針類型的數據,使其指向某個地址單元。
#include<iostream>
int *newInt(int value);//1聲明一個函數(前邊*newInt沒有用括號括起來,說明是一個函數,括起來的話就是一個指針了),這個函數前邊加一個*號,說明他的返回值類型是int*,是一個指向整型變量的一個地址,一個指針
int main()
{int *x = newInt(20);//2、6定義一個指針變量x,這是一個指向整數的指針變量,他賦值為newInt(20)這個函數的返回值,因為這個函數的返回值是一個地址,//因為聲明函數的時候加了一個*號是一個指針的標志,說明他的返回值是一個地址。調用他std::cout << *x<<std::endl;//7打印地址delete x;//8把他釋放掉x = NULL;//9把他的指針填充NULL,使得指針不會被隨意的亂用return 0;//然后結束整個程序
}
int *newInt(int value)
{int *myInt = new int;//3這個里邊它new一個整型的變量,在堆里邊申請一個整型的內存塊*myInt = value;//4然后給他賦值,賦值為這個參數value的值(*myInt使用*給他解引用)return myInt;//5賦值之后返回他的地址(6處接到地址)
}
-----副本構造器
?
36-38
總結
以上是生活随笔為你收集整理的C++:随笔7---运算符重载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (曲率系列3:)PCL:PCL库中的两种
- 下一篇: C++中的.hpp理解