Professors hammered it into my head when I was in school, associates have jumped down my throat for it on code reviews, and it's in pretty much every C++ textbook out there: "accessor" (aka "selector" or "getter") methods must be marked const. If it doesn't change or mutate the data, then mark it const.
Why? How could the invocation of an accessor modify the private data?
In the following example, I have set up a simple class and one accessor. How can getBrand() be used to modify the private data? In my eyes, it can't; so why do we need to mark it const?
In other words, am I correct in saying that it is impossible for getBrand() to be used, in practice, to mutate a private property?
Example:
#include <iostream>
#include <string>
class Cheese {
public:
Cheese(std::string brand):brand_(brand) {}
std::string getBrand() { return brand_; } // Intentionally not marked const
private:
std::string brand_;
};
int main() {
Cheese cheddar("Cabot clothbound");
std::cout << "Brand: " << cheddar.getBrand() << std::endl;
return 0;
}
It's actually very simple: If the method is not const, you would not be able to use it on const objects - but you do want to be able to use it. With your class, you can't implement
void print_brand(const Cheese& cheese);
(unless you const-cast, which you shouldn't do).
Also, if you do make it const, instead of returning a copy of your string - which may or may not get optimized away, you could implement:
const std::string& getBrand() const { return brand_; }
which returns a reference, or perhaps
std::string_view getBrand() const { return brand_; }
that does not "commit" your API to the string class (read about string_view here; it was only officially added to the language in C++17, but is available as std::experimental::string_view with recent compilers).
where is the vulnerability?
The answer is that function names can lie, but interfaces containing references to const cannot.
example:
#include <iostream>
#include <string>
// this function name lies
void i_wont_touch_your_cheese(std::string& s)
{
// uh-oh - I lied!
s = "lol, I touched your cheese!";
}
// this one cannot. The compiler won't allow it
void i_really_wont_touch_your_cheese(const std::string& s)
{
// compiler error here!
// cheese is safe
s = "lol, I touched your cheese!";
}
int main() {
auto cheese = std::string("untouched cheese");
i_wont_touch_your_cheese(cheese);
std::cout << cheese << std::endl;
cheese = "more untouched cheese";
i_really_wont_touch_your_cheese(cheese);
std::cout << cheese << std::endl;
return 0;
}
Strictly speaking the method you've written doesn't mutate class members. If you marked it const the compiler completely prevents it from mutating members. But let's dig a little deeper here.
Typically you write code once but read/review it many times. Marking a method const allows future readers to look at it and instantly know that the method can't change the class state because the compiler would catch it. For example if you accidentally write size_t empty() const { return size_ = 0; } (where size_ is a member variable) the compiler will catch your typo. If you hadn't marked the method const you would have a subtle bug.
But more importantly, const methods can only call other const methods. Consider if you have a method that takes the class state as input, does a bunch of work and returns a result. If the getter methods it uses to do its work are non-const then the long, complicated method also has to be non-const which then makes code comprehension much harder.
Function with keyword const guarantee that use her, don't change object which was given. So if you want give to print object to some function or especially to operator<< outside class you should use only method with keyword const.
std::ostream &operator<<(std::ostream& str, const Object& obj)
{
return str << obj.someFunctionConst() << std::endl;
}
function with error (compile error)
std::ostream &operator<<(std::ostream& str, const Object& obj)
{
return str << obj.someFunctionWithoutConst() << std::endl;
}
const is not about vulneratilities! Thecnically it doesn't enforce security, safety or anything. Nothing stops you from applying const-cast to remove it or using keyword mutable to change something.
const helps to make your code cleaner. See Const-Correctness article with good explanation of the concept.
When you look at something with const you would reasonably expect it not change the state. The declaration int Class:foo() const; tells me foo() won't change the object's state and it is ok to call it from anywhere.
const is infectious. The more you use it the more you have to use it. bool Class::bar(); would have to be const if it needs to call foo().
Same applies in reverse direction. If you are passing const & to a function: void zoo(const Class &cls); then you know zoo() won't be able to modify cls's state.
Related
I ran into a nasty bug in some of my code. Here's the simplified version:
#include <iostream>
class A
{
public:
std::string s;
void run(const std::string& x)
{
// do some "read-only" stuff with "x"
std::cout << "x = " << x << std::endl;
// Since I passed X as a const referece, I expected string never to change
// but actually, it does get changed by clear() function
clear();
// trying to do something else with "x",
// but now it has a different value although I declared it as
// "const". This killed the code logic.
std::cout << "x = " << x << std::endl;
// is there some way to detect possible change of X here during compile-time?
}
void clear()
{
// in my actual code, this doesn't even happen here, but 3 levels deep on some other code that gets called
s.clear();
}
};
int main()
{
A a;
a.s = "test";
a.run(a.s);
return 0;
}
Basically, the code that calls a.run() use to be used for all kinds of strings in the past and at one point, I needed the exact value that object "a.s" had, so I just put a.s in there and then some time later noticed program behaving weird. I tracked it down to this.
Now, I understand why this is happening, but it looks like one of those really hard to trace and detect bugs. You see the parameter declared as const & and suddenly it's value changes.
Is there some way to detect this during compile-time? I'm using CLang and MSVC.
Thanks.
Is there some way to detect this during compile-time?
I don't think so. There is nothing inherently wrong about modifying a member variable that is referred by a const reference, so there is no reason for the compiler to warn about it. The compiler cannot read your mind to find out what your expectations are.
There are some usages where such wrong assumption could result in definite bugs such as undefined behaviour that could be diagnosed if identified. I suspect that identifying such cases in general would be quite expensive computationally, so I wouldn't rely on it.
Redesigning the interface could make that situation impossible For example following:
struct wrapper {
std::string str;
};
void run(const wrapper& x);
x.str will not alias the member because the member is not inside a wrapper.
I have stumbled upon an error for which I cannot grasp the reason.
I think it basically gets down to this error:
error: no matching function for call to ‘std::basic_ostream<char>::operator<<(const std::basic_string<char>&)’
I looked into specification on www.cplusplus.com and indeed it says there is no definition for std::ostream::operator<< with std::string as an argument.
My question is, what happens when one writes std_ostream_instance << std_fancy_string;. I believe it is one of the most common invocations ( e.g. std::out << std::string("Hello world!") ) next to const char*.
The error originates from these lines:
template<typename T>
void Log::_log(const T& msg)
{ _sink->operator<<( msg ); }
_sink is defied as std::ostream*
There are some wrapping functions around but it breaks here.
I think I could work around by writing
template<>
void Log::_log<std::string>(const std::string& msg) {
_sink->operator<<( msg.c_str() );
}
since there is ostream& operator<< (ostream& out, const unsigned char* s ); defined by default.
I just see no reason why it is not guessed automatically since it clearly works in simple use like cout << any_std_string.
Not sure if this is relevant but I want to be able to pass down through my log functions anything than can be handled by std::ostream. I used explicit non-templated declarations but decided to move to template for log(const T& anything_to_log) to refacator it. It seemed plain stupid to have 5+ overloads. I get the error when I try compiling something like Log::log( std::string("test case") ).
It looks like something stupid-simple but I cannot get it on my own. Tried to google and search stack to no avail.
With regards, luk32.
PS. I checked the work-around and it works. Why it's not implicitly done ?
operator << overloads are not members of ostream. They are freestanding functions, for example
ostream& operator << ( ostream& out, const basic_string<T>& bs );
Try
template<typename T>
void Log::_log(const T& msg)
{ *_sink << msg; }
The std::string version is not a member function, so can't be called as a member of _sink. Try it this way to pick up both member and non-member versions (in fact it is unlikely you will need the member versions at all anyway):
#include <iostream>
#include <string>
int main()
{
std::ostream * os = &std::cout;
std::string s = "Hello\n";
// This will not work
// os->operator<<(s);
(*os) << s;
return 0;
}
Or better yet would be to store _sink as a reference, and output exactly as you normally would to cout.
Is there a (more or less at least) standard int class for c++?
If not so, is it planned for say C++13 and if not so, is there any special reasons?
OOP design would benefit from it I guess, like for example it would be nice to have an assignment operator in a custom class that returns an int:
int i=myclass;
and not
int i=myclass.getInt();
OK, there are a lot of examples where it could be useful, why doesn't it exist (if it doesn't)?
It is for dead reckoning and other lag-compensating schemes and treating those values as 'normal' variables will be nice, hopefully anyway!.
it would be nice to have an assignment operator in a custom class that returns an int
You can do that with a conversion operator:
class myclass {
int i;
public:
myclass() : i(42) {}
// Allows implicit conversion to "int".
operator int() {return i;}
};
myclass m;
int i = m;
You should usually avoid this, as the extra implicit conversions can introduce ambiguities, or hide category errors that would otherwise be caught by the type system. In C++11, you can prevent implicit conversion by declaring the operator explicit; then the class can be used to initialise the target type, but won't be converted implicitly:
int i(m); // OK, explicit conversion
i = m; // Error, implicit conversion
If you want to allow your class to implicitly convert to int, you can use an implicit conversion operator (operator int()), but generally speaking implicit conversions cause more problems and debugging than they solve in ease of use.
If your class models an int, then the conversion operator solution presented by other answers is fine, I guess. However, what does your myclass model?
What does it mean to get an integer out of it?
That's what you should be thinking about, and then you should come to the conclusion that it's most likely meaningless to get an integer without any information what it represents.
Take std::vector<T>::size() as an example. It returns an integer. Should std::vector<T> be convertible to an integer for that reason? I don't think so. Should the method be called getInt()? Again, I don't think so. What do you expect from a method called getInt()? From the name alone, you learn nothing about what it returns. Also, it's not the only method that returns an integer, there's capacity() too.
Implement operator int () for your class
This can be realized by the cast operator. E.g:
class MyClass {
private:
int someint;
public:
operator const int() {
return this->someint;
}
}
No there isn't any standard int class. For things such as BigDecimal you can look at Is there a C++ equivalent to Java's BigDecimal?
As for int, if you really need it, you can create your own. I have never come across an instance where I needed an Integer class.
No, and there won't be any. What you want to do can be done with conversion operator:
#include <iostream>
struct foo {
int x;
foo(int x) : x(x) {}
operator int() { return x; }
};
int main() {
foo x(42);
int y(x);
std::cout << y;
}
No, and there probably won't be.
int i=myclass;
This is covered by conversion operators:
struct MyClass {
operator int() {
return v;
}
int v;
} myclass = {2};
int i = myclass; // i = 2
Not everything has to be 'object oriented'. C++ offers other options.
There are obvious reasons to have a class for int, because int by itself does not allow for the absence of any value. Take for instance a JSON message. It can contain the definition for an object named “foo”, and an integer named “bar”, for example:
{"foo": {"bar": 0}}
Which has the meaning that “bar" is equal to 0 (zero), but if you omit “bar”, like this:
{"foo": {}}
Now it takes on the meaning that “bar” is non-existent, which is a completely different meaning and cannot be represented by int alone. In the old days, if this situation arose, some programmers would use a separate flag, or use a specific integer value to signify that the value was not supplied, or undefined, or non-existent. But whatever you call it, a better way is to have a class for integer which defines the functionality and makes it reusable and consistent.
Another case would be a database table that has an integer column added some time after it’s creation. Records that were added prior to when the new column was added will return null, meaning no value present, and records added after the column’s creation would return a value. You may need to take a different action for null value vs. 0 (zero).
So here's the beginnings of what a class for int or string might look like. But before we get to the code, let's look at the usage as that is why you would create the class in the first place, to make your life easier in the long run.
int main(int argc, char **argv) {
xString name;
xInt age;
std::cout<< "before assignment:" << std::endl;
std::cout<< "name is " << name << std::endl;
std::cout<< "age is " << age << std::endl;
// some data collection/transfer occurs
age = 32;
name = "john";
// data validation
if (name.isNull()) {
throw std::runtime_error("name was not supplied");
}
if (age.isNull()) {
throw std::runtime_error("age was not supplied");
}
// data output
std::cout<< std::endl;
std::cout<< "after assignment:" << std::endl;
std::cout<< "name is " << name << std::endl;
std::cout<< "age is " << age << std::endl;
return 0;
}
Here is the sample output from the program:
before assignment:
name is null
age is null
after assignment:
name is john
age is 32
Note that when the instance of the xInt class has not been assigned a value, the << operator automatically prints "null" instead of zero, and the same applies to xString for name. What you do here is totally up to you. For instance, you might decide to print nothing instead of printing “null”. Also, for the sake of brevity, I've hard coded the assignments. In the real world, you would be gathering/parsing data from a file or client connection, where that process would either set (or not set) the data values according to what is found in the input data. And of course, this program won't actually ever throw the runtime exceptions, but I put them there to give you a flavor of how you might throw the errors. So, one might say, well, why don't you just throw the exception in your data collection process? Well, the answer to that is, with the eXtended class variables (xInt & xString), we can write a generic, reusable, data gathering process and then just examine the data that is returned in our business logic where we can then throw appropriate errors based on what we find.
Ok, so here's the class code to go with the above main method:
#include <iostream>
#include <string>
class xInt {
private:
int _value=0;
bool _isNull=true;
public:
xInt(){}
xInt(int value) {
_value=value;
_isNull=false;
}
bool isNull(){return _isNull;}
int value() {return _value;}
void unset() {
_value=0;
_isNull=true;
}
friend std::ostream& operator<<(std::ostream& os, const xInt& i) {
if (i._isNull) {
os << "null";
} else {
os << i._value;
}
return os;
}
xInt& operator=(int value) {
_value=value;
_isNull=false;
return *this;
}
operator const int() {
return _value;
}
};
class xString {
private:
std::string _value;
bool _isNull=true;
public:
xString(){}
xString(int value) {
_value=value;
_isNull=false;
}
bool isNull() {return _isNull;}
std::string value() {return _value;}
void unset() {
_value.clear();
_isNull=true;
}
friend std::ostream& operator<<(std::ostream& os, const xString& str) {
if (str._isNull) {
os << "null";
} else {
os << str._value;
}
return os;
}
xString& operator<<(std::ostream& os) {
os << _value;
return *this;
}
xString& operator=(std::string value) {
_value.assign(value);
_isNull=false;
return *this;
}
operator const std::string() {
return _value;
}
};
Some might say, wow, that's pretty ugly compared to just saying int or string, and yes, I agree that it's pretty wordy, but remember, you only write the base class once, and then from then on, your code that you're reading and writing every day would look more like the main method that we first looked at, and that is very concise and to the point, agreed? Next you'll want to learn how to build shared libraries so you can put all these generic classes and functionality into a re-usable .dll or .so so that you're only compiling the business logic, not the entire universe. :)
There's no reason to have one, and so there won't be any.
Your cast operator should realize this
An example
class MyClass {
private:
int someint;
public:
operator const int() {
return this->someint;
}
}
I would like to be able to do:
foo(stringstream()<<"number = " << 500);
EDIT: single line solution is crucial since this is for logging purposes. These will be all around the code.
inside foo will print the string to screen or something of the sort.
now since stringstream's operator<< returns ostream&, foo's signature must be:
foo(ostream& o);
but how can I convert ostream& to string? (or char*).
Different approaches to achieving this use case are welcome as well.
The obvious solution is to use dynamic_cast in foo. But the given
code still won't work. (Your example will compile, but it won't do what
you think it should.) The expression std::ostringstream() is a
temporary, you can't initialize a non-const reference with a temporary,
and the first argument of std::operator<<( std::ostream&, char const*)
is a non-const reference. (You can call a member function on a
temporary. Like std::ostream::operator<<( void const* ). So the code
will compile, but it won't do what you expect.
You can work around this problem, using something like:
foo( std::ostringstream().flush() << "number = " << 500 );
std::ostream::flush() returns a non-const reference, so there are no
further problems. And on a freshly created stream, it is a no-op.
Still, I think you'll agree that it isn't the most elegant or intuitive
solution.
What I usually do in such cases is create a wrapper class, which
contains it's own std::ostringstream, and provides a templated
member operator<< which forwards to the contained
std::ostringstream. Your function foo would take a const
reference to this—or what I offen do is have the destructor call
foo directly, so that the client code doesn't even have to worry about
it; it does something like:
log() << "number = " << 500;
The function log() returns an instance of the wrapper class (but see
below), and the (final) destructor of this class calls your function
foo.
There is one slight problem with this. The return value may be copied,
and destructed immediately after the copy. Which will wreck havoc with
what I just explained; in fact, since std::ostringstream isn't
copyable, it won't even compile. The solution here is to put all of the
actual logic, including the instance of std::ostringstream and the
destructor logic calling foo in a separate implementation class, have
the public wrapper have a boost::shared_ptr to it, and forward. Or
just reimplement a bit of the shared pointer logic in your class:
class LogWrapper
{
std::ostringstream* collector;
int* useCount;
public:
LogWrapper()
: collector(new std::ostringstream)
, useCount(new int(1))
{
}
~LogWrapper()
{
-- *useCount;
if ( *useCount == 0 ) {
foo( collector->str() );
delete collector;
delete useCount;
}
}
template<typename T>
LogWrapper& operator<<( T const& value )
{
(*collector) << value;
return *this;
}
};
Note that it's easy to extend this to support optional logging; just
provide a constructor for the LogWrapper which sets collector to
NULL, and test for this in the operator<<.
EDITED:
One other thing occurs to me: you'll probably want to check whether the
destructor is being called as a result of an exception, and not call
foo in that case. Logically, I'd hope that the only exception you
might get is std::bad_alloc, but there will always be a user who
writes something like:
log() << a + b;
where the + is a user defined overload which throws.
I would suggest you to use this utility struct:
struct stringbuilder
{
std::stringstream ss;
template<typename T>
stringbuilder & operator << (const T &data)
{
ss << data;
return *this;
}
operator std::string() { return ss.str(); }
};
And use it as:
void f(const std::string & s );
int main()
{
char const *const pc = "hello";
f(stringbuilder() << '{' << pc << '}' );
//this is my most favorite line
std::string s = stringbuilder() << 25 << " is greater than " << 5 ;
}
Demo (with few more example) : http://ideone.com/J995r
More on my blog : Create string on the fly just in one line
You could use a proxy object for this; this is a bit of framework, but if you want to use this notation in a lot of places then it may be worth it:
#include <iostream>
#include <sstream>
static void foo( std::string const &s )
{
std::cout << s << std::endl;
}
struct StreamProxy
{
std::stringstream stream;
operator std::string() { return stream.str(); }
};
template <typename T>
StreamProxy &operator<<( StreamProxy &s, T v )
{
s.stream << v;
return s;
}
static StreamProxy make_stream()
{
return StreamProxy();
}
int main()
{
foo( make_stream() << "number = " << 500 );
}
This program prints
number = 500
The idea is to have a little wrapper class which can be implicitely converted into a std::string. The << operator is simply forwarded to the contained std::stringstream. The make_stream() function is strictly speaking not necessary (you could also say StreamProxy(), but I thought it looks a bit nicer.
A couple of options other than the nice proxy solution just presented by Frerich Raabe:
Define a static string stream variable in the header that defines the logging function and use the comma operator in your invocation of the logging function so that this variable is passed rather than the ostream& returned by the stream insertion operator. You can use a logging macro to hide this ugliness. The problem with this solution is that it is a bit on the ugly side, but this is a commonly used approach to logging.
Don't use C++ I/O. Use a varargs C-style solution instead. Pass a format string as the first argument, with the remaining arguments being targets for that format string. A problem with this solution is that even if your compiler is smart enough to ensure that printf and its cousins are safe, the compiler probably won't know that this new function is a part of the printf family. Nonetheless, this is also a commonly used approach.
If you don't mind using macros functions, you can make the logging function accept const string&, and use the following macro
#define build_string(expr) \
(static_cast<ostringstream*>(&(ostringstream().flush() << expr))->str())
And suppose you foo has signature void foo(const string&), you only need the one-liner
foo(build_string("number = " << 500))
This was inspired by James Kanze's answer about static_cast and stringstream.flush. Without the .flush() the above method fails with unexpected output.
Please note that this method should not leak memory, as temporary values, whether in the pointer form or not, are still allocated on the stack and hence destroyed upon return.
Since you're converting to string anyways, why not
void foo(const std::string& s)
{
std::cout << "foo: " << s << std::endl;
}
...
std::stringstream ss;
ss << "number = " << 500;
foo(ss.str());
This is not possible. As the name ostream implies, it is used for output, for writing to it. You could change the parameter to stringstream&. This class has the method str() which returns a std::string for your use.
EDIT I did not read the issue with operator << returning ostream&. So I guess you cannot simply write your statements within the functions argument list but have to write it before.
You can create a small wrapper around std::ostringstream that will convert back to std::string on use, and have the function take a std::string const &. The first approach to this solution can be found in this answer to a different question.
On top of that, you can add support for manipulators (std::hex) if needed.
What is the difference between these two terms, and why do I need mutable?
"Physical" constness comes from declaring an object const, and could, in principle, be enforced by placing the object in read-only memory, so it cannot change. Attempting to change it will cause undefined behaviour; it might change, or it might not, or it might trigger a protection fault, or it might melt the memory chip.
"Logical" constness comes from declaring a reference or pointer const, and is enforced by the compiler. The object itself may or may not be "physically" const, but the reference cannot be used to modify it without a cast. If the object is not "physically" const, then C++ allows you to modify it, using const_cast to circumvent the protection.
A mutable class member can be modified even if the class object itself (or the reference or pointer used to access it) is const. Examples of good uses of this are a mutex that must be locked during a read operation, and a cache to store the result of an expensive read operation. In both cases, the operation itself should be a const function (since it doesn't affect the visible state of the object), but it needs to modify the mutex or the cache, so these need to be mutable. It can also be abused to make the object visibly change when logically it shouldn't, so use it with care; only declare members mutable if they don't form part of the externally visible state.
You need mutable when there are fields in an object that can be considered "internal", i.e. any external user of the class cannot determine the value of these fields. The class might need to write to these fields even though the instance is considered constant, i.e. not changing.
Consider a hard drive; its cache is an example of such state. The cache is written to when data is read from the actual disk.
This is not possible to express cleanly in C++ without making the corresponding members mutable, to allow them to be changed even in methods marked const. As pointed out in a comment, you can always reach for the hammer and use a const_cast<> to remove the const-ness, but that is of course cheating. :)
Scott Meyers, Effective C++, Item 3:
Use const whenever possible
has an excellent discussion (with examples) on this topic. Its hard to write better than Scott!
Note also that physical-constness is also known as bitwise-constness.
Some code for people who want to try out the effects of physical constness theirselfes:
#include <type_traits>
using namespace std;
using type = int const;
type &f();
int main()
{
const_cast<remove_const_t<type> &>(f()) = 123;
}
#if defined(__GNUC__) || defined(__llvm__)
__attribute__((noinline))
#elif defined(_MSC_VER)
__declspec(noinline)
#endif
type &f()
{
static type i = 0;
return i;
}
Under Linux and Windows this program crashes until you drop the const-specifier with type.
I respectfully disagree with Mike Seymour's answer:
A member function of a class can be defined as a const function, meaning that it will not modify any internal state of the object (except for members that are marked mutable, which becomes an important issue when we talk later about "logical" const-ness). That is the actual definition of "physical" const-ness. But even though a function is marked const, it may be logically modifying the objects state. Consider the following example:
#include <iostream>
using namespace std;
class Foo
{
private:
int *ip;
public:
Foo(int i) {
ip = new int(i);
}
virtual ~Foo() {
delete ip;
}
void modifyState(int i) const {
*ip = i;
}
int getI(void) const {
return *ip;
}
};
int main(int argc, char *argv[])
{
Foo foo(7);
cout << foo.getI() << endl;
foo.modifyState(9);
cout << foo.getI() << endl;
return 0;
}
Prints:
7
9
Member function modifyState does not modify member ip, so it satisfies the requirements for physical const-ness. But who can argue that since it is modifying what ip points to that the logical state has not been changed? So even though this function satisfies the requirements for physical cont-ness, it does not satisfy logical const-ness since what function getI will return has been changed by the call to modifyState.
Conversely, we can physically modify an object's member, but the state of the object as it appears to the outside world via its interface has not changed. Consider the following example where const functions getCelsiusTemperature and getFahrenheitTemperature modify mutable state but do not modify the object's logical state:
#include <iostream>
using namespace std;
class Temperature
{
private:
mutable float temperature;
mutable bool is_celsius;
public:
Temperature(double temperature, bool is_celsius=true) {
this->temperature = temperature;
this->is_celsius = is_celsius;
}
double getCelsiusTemperature(void) const {
if (!this->is_celsius) {
// convert to Celsius:
this->temperature = (this->temperature - 32.0) * 5.0 / 9.0;
this->is_celsius = true;
}
return this->temperature;
}
const double getFahrenheitTemperature(void) const {
if (this->is_celsius) {
// convert to Fahrenheit:
this->temperature = this->temperature * 9.0 / 5.0 + 32.0;
this->is_celsius = false;
}
return this->temperature;
}
};
int main(int argc, char *argv[])
{
const Temperature t(100, true);
cout << t.getCelsiusTemperature() << endl;
cout << t.getFahrenheitTemperature() << endl;
cout << t.getCelsiusTemperature() << endl;
return 0;
}
Prints:
100
212
100